Add PackageCheck pretty print
[cabal.git] / Cabal / src / Distribution / PackageDescription / Check.hs
blob5def0b5421be363ecc636fc1d8ca1ffe45362a9a
1 -----------------------------------------------------------------------------
2 -- |
3 -- Module : Distribution.PackageDescription.Check
4 -- Copyright : Lennart Kolmodin 2008
5 -- License : BSD3
6 --
7 -- Maintainer : cabal-devel@haskell.org
8 -- Portability : portable
9 --
10 -- This has code for checking for various problems in packages. There is one
11 -- set of checks that just looks at a 'PackageDescription' in isolation and
12 -- another set of checks that also looks at files in the package. Some of the
13 -- checks are basic sanity checks, others are portability standards that we'd
14 -- like to encourage. There is a 'PackageCheck' type that distinguishes the
15 -- different kinds of checks so we can see which ones are appropriate to report
16 -- in different situations. This code gets used when configuring a package when
17 -- we consider only basic problems. The higher standard is used when
18 -- preparing a source tarball and by Hackage when uploading new packages. The
19 -- reason for this is that we want to hold packages that are expected to be
20 -- distributed to a higher standard than packages that are only ever expected
21 -- to be used on the author's own environment.
23 module Distribution.PackageDescription.Check (
24 -- * Package Checking
25 CheckExplanation(..),
26 PackageCheck(..),
27 checkPackage,
28 checkConfiguredPackage,
29 wrapParseWarning,
30 ppPackageCheck,
32 -- ** Checking package contents
33 checkPackageFiles,
34 checkPackageContent,
35 CheckPackageContentOps(..),
36 checkPackageFileNames,
37 ) where
39 import Distribution.Compat.Prelude
40 import Prelude ()
42 import Data.List (group)
43 import Distribution.CabalSpecVersion
44 import Distribution.Compat.Lens
45 import Distribution.Compiler
46 import Distribution.License
47 import Distribution.ModuleName (ModuleName)
48 import Distribution.Package
49 import Distribution.PackageDescription
50 import Distribution.PackageDescription.Configuration
51 import Distribution.Parsec.Warning (PWarning, showPWarning)
52 import Distribution.Pretty (prettyShow)
53 import Distribution.Simple.BuildPaths (autogenPathsModuleName)
54 import Distribution.Simple.BuildToolDepends
55 import Distribution.Simple.CCompiler
56 import Distribution.Simple.Glob
57 import Distribution.Simple.Utils hiding (findPackageDesc, notice)
58 import Distribution.System
59 import Distribution.Types.ComponentRequestedSpec
60 import Distribution.Types.PackageName.Magic
61 import Distribution.Utils.Generic (isAscii)
62 import Distribution.Verbosity
63 import Distribution.Version
64 import Distribution.Utils.Path
65 import Language.Haskell.Extension
66 import System.FilePath
67 (splitDirectories, splitExtension, splitPath, takeExtension, takeFileName, (<.>), (</>))
69 import qualified Data.ByteString.Lazy as BS
70 import qualified Data.Map as Map
71 import qualified Distribution.Compat.DList as DList
72 import qualified Distribution.SPDX as SPDX
73 import qualified System.Directory as System
75 import qualified System.Directory (getDirectoryContents)
76 import qualified System.FilePath.Windows as FilePath.Windows (isValid)
78 import qualified Data.Set as Set
79 import qualified Distribution.Utils.ShortText as ShortText
81 import qualified Distribution.Types.BuildInfo.Lens as L
82 import qualified Distribution.Types.GenericPackageDescription.Lens as L
83 import qualified Distribution.Types.PackageDescription.Lens as L
85 -- $setup
86 -- >>> import Control.Arrow ((&&&))
88 -- ------------------------------------------------------------
89 -- * Warning messages
90 -- ------------------------------------------------------------
92 -- | Which stanza does `CheckExplanation` refer to?
94 data CEType = CETLibrary | CETExecutable | CETTest | CETBenchmark
95 deriving (Eq, Ord, Show)
97 -- | Pretty printing `CEType`.
99 ppCE :: CEType -> String
100 ppCE CETLibrary = "library"
101 ppCE CETExecutable = "executable"
102 ppCE CETTest = "test suite"
103 ppCE CETBenchmark = "benchmark"
105 -- | Which field does `CheckExplanation` refer to?
107 data CEField = CEFCategory | CEFMaintainer | CEFSynopsis
108 | CEFDescription | CEFSynOrDesc
109 deriving (Eq, Ord, Show)
111 -- | Pretty printing `CEField`.
113 ppCEField :: CEField -> String
114 ppCEField CEFCategory = "category"
115 ppCEField CEFMaintainer = "maintainer"
116 ppCEField CEFSynopsis = "synopsis"
117 ppCEField CEFDescription = "description"
118 ppCEField CEFSynOrDesc = "synopsis' or 'description"
120 -- | Explanations of 'PackageCheck`'s errors/warnings.
122 data CheckExplanation =
123 ParseWarning FilePath PWarning
124 | NoNameField
125 | NoVersionField
126 | NoTarget
127 | UnnamedInternal
128 | DuplicateSections [UnqualComponentName]
129 | IllegalLibraryName PackageDescription
130 | NoModulesExposed Library
131 | SignaturesCabal2
132 | AutogenNotExposed
133 | AutogenIncludesNotIncluded
134 | NoMainIs Executable
135 | NoHsLhsMain
136 | MainCCabal1_18
137 | AutogenNoOther CEType UnqualComponentName
138 | AutogenIncludesNotIncludedExe
139 | TestsuiteTypeNotKnown TestType
140 | TestsuiteNotSupported TestType
141 | BenchmarkTypeNotKnown BenchmarkType
142 | BenchmarkNotSupported BenchmarkType
143 | NoHsLhsMainBench
144 | InvalidNameWin PackageDescription
145 | ZPrefix
146 | NoBuildType
147 | NoCustomSetup
148 | UnknownCompilers [String]
149 | UnknownLanguages [String]
150 | UnknownExtensions [String]
151 | LanguagesAsExtension [String]
152 | DeprecatedExtensions [(Extension, Maybe Extension)]
153 | MissingField CEField
154 | SynopsisTooLong
155 | ShortDesc
156 | InvalidTestWith [Dependency]
157 | ImpossibleInternalDep [Dependency]
158 | ImpossibleInternalExe [ExeDependency]
159 | MissingInternalExe [ExeDependency]
160 | NONELicense
161 | NoLicense
162 | AllRightsReservedLicense
163 | LicenseMessParse PackageDescription
164 | UnrecognisedLicense String
165 | UncommonBSD4
166 | UnknownLicenseVersion License [Version]
167 | NoLicenseFile
168 | UnrecognisedSourceRepo String
169 | MissingType
170 | MissingLocation
171 | MissingModule
172 | MissingTag
173 | SubdirRelPath
174 | SubdirGoodRelPath String
175 | OptFasm String
176 | OptViaC String
177 | OptHpc String
178 | OptProf String
179 | OptO String
180 | OptHide String
181 | OptMake String
182 | OptMain String
183 | OptONot String
184 | OptOOne String
185 | OptOTwo String
186 | OptSplitSections String
187 | OptSplitObjs String
188 | OptWls String
189 | OptExts String
190 | OptThreaded String
191 | OptRts String
192 | OptWithRts String
193 | COptONumber String String
194 | COptCPP String
195 | OptAlternatives String String [(String, String)]
196 | RelativeOutside String FilePath
197 | AbsolutePath String FilePath
198 | BadRelativePAth String FilePath String
199 | DistPoint (Maybe String) FilePath
200 | GlobSyntaxError String String
201 | InvalidOnWin [FilePath]
202 | FilePathTooLong FilePath
203 | FilePathNameTooLong FilePath
204 | FilePathSplitTooLong FilePath
205 | FilePathEmpty
206 | CVTestSuite
207 | CVDefaultLanguage
208 | CVDefaultLanguageComponent
209 | CVExtraDocFiles
210 | CVMultiLib
211 | CVReexported
212 | CVMixins
213 | CVExtraFrameworkDirs
214 | CVDefaultExtensions
215 | CVExtensionsDeprecated
216 | CVSources
217 | CVExtraDynamic [[String]]
218 | CVVirtualModules
219 | CVSourceRepository
220 | CVExtensions CabalSpecVersion [Extension]
221 | CVCustomSetup
222 | CVExpliticDepsCustomSetup
223 | CVAutogenPaths
224 | GlobNoMatch String String
225 | GlobExactMatch String String FilePath
226 | GlobNoDir String String FilePath
227 | UnknownOS [String]
228 | UnknownArch [String]
229 | UnknownCompiler [String]
230 | BaseNoUpperBounds
231 | SuspiciousFlagName [String]
232 | DeclaredUsedFlags (Set FlagName) (Set FlagName)
233 | NonASCIICustomField [String]
234 | RebindableClash
235 | WErrorUnneeded String
236 | JUnneeded String
237 | FDeferTypeErrorsUnneeded String
238 | DynamicUnneeded String
239 | ProfilingUnneeded String
240 | UpperBoundSetup String
241 | DuplicateModule String [ModuleName]
242 | PotentialDupModule String [ModuleName]
243 | BOMStart FilePath
244 | NotPackageName FilePath String
245 | NoDesc
246 | MultiDesc [String]
247 | UnknownFile String (SymbolicPath PackageDir LicenseFile)
248 | MissingSetupFile
249 | MissingConfigureScript
250 | UnknownDirectory String FilePath
251 | MissingSourceControl
252 deriving (Eq, Ord, Show)
254 -- | Wraps `ParseWarning` into `PackageCheck`.
256 wrapParseWarning :: FilePath -> PWarning -> PackageCheck
257 wrapParseWarning fp pw = PackageDistSuspicious (ParseWarning fp pw)
258 -- TODO: as Jul 2022 there is no severity indication attached PWarnType.
259 -- Once that is added, we can output something more appropriate
260 -- than PackageDistSuspicious for every parse warning.
261 -- (see: Cabal-syntax/src/Distribution/Parsec/Warning.hs)
263 -- | Pretty printing `CheckExplanation`.
265 ppExplanation :: CheckExplanation -> String
266 ppExplanation (ParseWarning fp pp) = showPWarning fp pp
267 ppExplanation NoNameField = "No 'name' field."
268 ppExplanation NoVersionField = "No 'version' field."
269 ppExplanation NoTarget =
270 "No executables, libraries, tests, or benchmarks found. Nothing to do."
271 ppExplanation UnnamedInternal =
272 "Found one or more unnamed internal libraries. Only the non-internal"
273 ++ " library can have the same name as the package."
274 ppExplanation (DuplicateSections duplicateNames) =
275 "Duplicate sections: "
276 ++ commaSep (map unUnqualComponentName duplicateNames)
277 ++ ". The name of every library, executable, test suite,"
278 ++ " and benchmark section in the package must be unique."
279 ppExplanation (IllegalLibraryName pkg) =
280 "Illegal internal library name "
281 ++ prettyShow (packageName pkg)
282 ++ ". Internal libraries cannot have the same name as the package."
283 ++ " Maybe you wanted a non-internal library?"
284 ++ " If so, rewrite the section stanza"
285 ++ " from 'library: '" ++ prettyShow (packageName pkg)
286 ++ "' to 'library'."
287 ppExplanation (NoModulesExposed lib) =
288 showLibraryName (libName lib) ++ " does not expose any modules"
289 ppExplanation SignaturesCabal2 =
290 "To use the 'signatures' field the package needs to specify "
291 ++ "at least 'cabal-version: 2.0'."
292 ppExplanation AutogenNotExposed =
293 "An 'autogen-module' is neither on 'exposed-modules' or 'other-modules'."
294 ppExplanation AutogenIncludesNotIncluded =
295 "An include in 'autogen-includes' is neither in 'includes' or "
296 ++ "'install-includes'."
297 ppExplanation (NoMainIs exe) =
298 "No 'main-is' field found for executable " ++ prettyShow (exeName exe)
299 ppExplanation NoHsLhsMain =
300 "The 'main-is' field must specify a '.hs' or '.lhs' file "
301 ++ "(even if it is generated by a preprocessor), "
302 ++ "or it may specify a C/C++/obj-C source file."
303 ppExplanation MainCCabal1_18 =
304 "The package uses a C/C++/obj-C source file for the 'main-is' field. "
305 ++ "To use this feature you need to specify 'cabal-version: 1.18' or"
306 ++ " higher."
307 ppExplanation (AutogenNoOther ct ucn) =
308 "On " ++ ppCE ct ++ " '" ++ prettyShow ucn ++ "' an 'autogen-module'"
309 ++ " is not on 'other-modules'"
310 ppExplanation AutogenIncludesNotIncludedExe =
311 "An include in 'autogen-includes' is not in 'includes'."
312 ppExplanation (TestsuiteTypeNotKnown tt) =
313 quote (prettyShow tt) ++ " is not a known type of test suite. "
314 ++ "Either remove the 'type' field or use a known type. "
315 ++ "The known test suite types are: "
316 ++ commaSep (map prettyShow knownTestTypes)
317 ppExplanation (TestsuiteNotSupported tt) =
318 quote (prettyShow tt) ++ " is not a supported test suite version. "
319 ++ "Either remove the 'type' field or use a known type. "
320 ++ "The known test suite types are: "
321 ++ commaSep (map prettyShow knownTestTypes)
322 ppExplanation (BenchmarkTypeNotKnown tt) =
323 quote (prettyShow tt) ++ " is not a known type of benchmark. "
324 ++ "Either remove the 'type' field or use a known type. "
325 ++ "The known benchmark types are: "
326 ++ commaSep (map prettyShow knownBenchmarkTypes)
327 ppExplanation (BenchmarkNotSupported tt) =
328 quote (prettyShow tt) ++ " is not a supported benchmark version. "
329 ++ "Either remove the 'type' field or use a known type. "
330 ++ "The known benchmark types are: "
331 ++ commaSep (map prettyShow knownBenchmarkTypes)
332 ppExplanation NoHsLhsMainBench =
333 "The 'main-is' field must specify a '.hs' or '.lhs' file "
334 ++ "(even if it is generated by a preprocessor)."
335 ppExplanation (InvalidNameWin pkg) =
336 "The package name '" ++ prettyShow (packageName pkg) ++ "' is "
337 ++ "invalid on Windows. Many tools need to convert package names to "
338 ++ "file names so using this name would cause problems."
339 ppExplanation ZPrefix =
340 "Package names with the prefix 'z-' are reserved by Cabal and "
341 ++ "cannot be used."
342 ppExplanation NoBuildType =
343 "No 'build-type' specified. If you do not need a custom Setup.hs or "
344 ++ "./configure script then use 'build-type: Simple'."
345 ppExplanation NoCustomSetup =
346 "Ignoring the 'custom-setup' section because the 'build-type' is "
347 ++ "not 'Custom'. Use 'build-type: Custom' if you need to use a "
348 ++ "custom Setup.hs script."
349 ppExplanation (UnknownCompilers unknownCompilers) =
350 "Unknown compiler " ++ commaSep (map quote unknownCompilers)
351 ++ " in 'tested-with' field."
352 ppExplanation (UnknownLanguages unknownLanguages) =
353 "Unknown languages: " ++ commaSep unknownLanguages
354 ppExplanation (UnknownExtensions unknownExtensions) =
355 "Unknown extensions: " ++ commaSep unknownExtensions
356 ppExplanation (LanguagesAsExtension languagesUsedAsExtensions) =
357 "Languages listed as extensions: "
358 ++ commaSep languagesUsedAsExtensions
359 ++ ". Languages must be specified in either the 'default-language' "
360 ++ " or the 'other-languages' field."
361 ppExplanation (DeprecatedExtensions ourDeprecatedExtensions) =
362 "Deprecated extensions: "
363 ++ commaSep (map (quote . prettyShow . fst) ourDeprecatedExtensions)
364 ++ ". " ++ unwords
365 [ "Instead of '" ++ prettyShow ext
366 ++ "' use '" ++ prettyShow replacement ++ "'."
367 | (ext, Just replacement) <- ourDeprecatedExtensions ]
368 ppExplanation (MissingField cef) =
369 "No '" ++ ppCEField cef ++ "' field."
370 ppExplanation SynopsisTooLong =
371 "The 'synopsis' field is rather long (max 80 chars is recommended)."
372 ppExplanation ShortDesc =
373 "The 'description' field should be longer than the 'synopsis' field. "
374 ++ "It's useful to provide an informative 'description' to allow "
375 ++ "Haskell programmers who have never heard about your package to "
376 ++ "understand the purpose of your package. "
377 ++ "The 'description' field content is typically shown by tooling "
378 ++ "(e.g. 'cabal info', Haddock, Hackage) below the 'synopsis' which "
379 ++ "serves as a headline. "
380 ++ "Please refer to <https://cabal.readthedocs.io/en/stable/"
381 ++ "cabal-package.html#package-properties> for more details."
382 ppExplanation (InvalidTestWith testedWithImpossibleRanges) =
383 "Invalid 'tested-with' version range: "
384 ++ commaSep (map prettyShow testedWithImpossibleRanges)
385 ++ ". To indicate that you have tested a package with multiple "
386 ++ "different versions of the same compiler use multiple entries, "
387 ++ "for example 'tested-with: GHC==6.10.4, GHC==6.12.3' and not "
388 ++ "'tested-with: GHC==6.10.4 && ==6.12.3'."
389 ppExplanation (ImpossibleInternalDep depInternalLibWithImpossibleVersion) =
390 "The package has an impossible version range for a dependency on an "
391 ++ "internal library: "
392 ++ commaSep (map prettyShow depInternalLibWithImpossibleVersion)
393 ++ ". This version range does not include the current package, and must "
394 ++ "be removed as the current package's library will always be used."
395 ppExplanation (ImpossibleInternalExe depInternalExecWithImpossibleVersion) =
396 "The package has an impossible version range for a dependency on an "
397 ++ "internal executable: "
398 ++ commaSep (map prettyShow depInternalExecWithImpossibleVersion)
399 ++ ". This version range does not include the current package, and must "
400 ++ "be removed as the current package's executable will always be used."
401 ppExplanation (MissingInternalExe depInternalExeWithImpossibleVersion) =
402 "The package depends on a missing internal executable: "
403 ++ commaSep (map prettyShow depInternalExeWithImpossibleVersion)
404 ppExplanation NONELicense = "The 'license' field is missing or is NONE."
405 ppExplanation NoLicense = "The 'license' field is missing."
406 ppExplanation AllRightsReservedLicense =
407 "The 'license' is AllRightsReserved. Is that really what you want?"
408 ppExplanation (LicenseMessParse pkg) =
409 "Unfortunately the license " ++ quote (prettyShow (license pkg))
410 ++ " messes up the parser in earlier Cabal versions so you need to "
411 ++ "specify 'cabal-version: >= 1.4'. Alternatively if you require "
412 ++ "compatibility with earlier Cabal versions then use 'OtherLicense'."
413 ppExplanation (UnrecognisedLicense l) =
414 quote ("license: " ++ l) ++ " is not a recognised license. The "
415 ++ "known licenses are: " ++ commaSep (map prettyShow knownLicenses)
416 ppExplanation UncommonBSD4 =
417 "Using 'license: BSD4' is almost always a misunderstanding. 'BSD4' "
418 ++ "refers to the old 4-clause BSD license with the advertising "
419 ++ "clause. 'BSD3' refers the new 3-clause BSD license."
420 ppExplanation (UnknownLicenseVersion lic known) =
421 "'license: " ++ prettyShow lic ++ "' is not a known "
422 ++ "version of that license. The known versions are "
423 ++ commaSep (map prettyShow known)
424 ++ ". If this is not a mistake and you think it should be a known "
425 ++ "version then please file a ticket."
426 ppExplanation NoLicenseFile = "A 'license-file' is not specified."
427 ppExplanation (UnrecognisedSourceRepo kind) =
428 quote kind ++ " is not a recognised kind of source-repository. "
429 ++ "The repo kind is usually 'head' or 'this'"
430 ppExplanation MissingType =
431 "The source-repository 'type' is a required field."
432 ppExplanation MissingLocation =
433 "The source-repository 'location' is a required field."
434 ppExplanation MissingModule =
435 "For a CVS source-repository, the 'module' is a required field."
436 ppExplanation MissingTag =
437 "For the 'this' kind of source-repository, the 'tag' is a required "
438 ++ "field. It should specify the tag corresponding to this version "
439 ++ "or release of the package."
440 ppExplanation SubdirRelPath =
441 "The 'subdir' field of a source-repository must be a relative path."
442 ppExplanation (SubdirGoodRelPath err) =
443 "The 'subdir' field of a source-repository is not a good relative path: "
444 ++ show err
445 ppExplanation (OptFasm fieldName) =
446 "'" ++ fieldName ++ ": -fasm' is unnecessary and will not work on CPU "
447 ++ "architectures other than x86, x86-64, ppc or sparc."
448 ppExplanation (OptViaC fieldName) =
449 "'" ++ fieldName ++": -fvia-C' is usually unnecessary. If your package "
450 ++ "needs -via-C for correctness rather than performance then it "
451 ++ "is using the FFI incorrectly and will probably not work with GHC "
452 ++ "6.10 or later."
453 ppExplanation (OptHpc fieldName) =
454 "'" ++ fieldName ++ ": -fhpc' is not necessary. Use the configure flag "
455 ++ " --enable-coverage instead."
456 ppExplanation (OptProf fieldName) =
457 "'" ++ fieldName ++ ": -prof' is not necessary and will lead to problems "
458 ++ "when used on a library. Use the configure flag "
459 ++ "--enable-library-profiling and/or --enable-profiling."
460 ppExplanation (OptO fieldName) =
461 "'" ++ fieldName ++ ": -o' is not needed. "
462 ++ "The output files are named automatically."
463 ppExplanation (OptHide fieldName) =
464 "'" ++ fieldName ++ ": -hide-package' is never needed. "
465 ++ "Cabal hides all packages."
466 ppExplanation (OptMake fieldName) =
467 "'" ++ fieldName
468 ++ ": --make' is never needed. Cabal uses this automatically."
469 ppExplanation (OptMain fieldName) =
470 "'" ++ fieldName ++ ": -main-is' is not portable."
471 ppExplanation (OptONot fieldName) =
472 "'" ++ fieldName ++ ": -O0' is not needed. "
473 ++ "Use the --disable-optimization configure flag."
474 ppExplanation (OptOOne fieldName) =
475 "'" ++ fieldName ++ ": -O' is not needed. "
476 ++ "Cabal automatically adds the '-O' flag. "
477 ++ "Setting it yourself interferes with the --disable-optimization flag."
478 ppExplanation (OptOTwo fieldName) =
479 "'" ++ fieldName ++ ": -O2' is rarely needed. "
480 ++ "Check that it is giving a real benefit "
481 ++ "and not just imposing longer compile times on your users."
482 ppExplanation (OptSplitSections fieldName) =
483 "'" ++ fieldName ++ ": -split-sections' is not needed. "
484 ++ "Use the --enable-split-sections configure flag."
485 ppExplanation (OptSplitObjs fieldName) =
486 "'" ++ fieldName ++ ": -split-objs' is not needed. "
487 ++ "Use the --enable-split-objs configure flag."
488 ppExplanation (OptWls fieldName) =
489 "'" ++ fieldName ++ ": -optl-Wl,-s' is not needed and is not portable to"
490 ++ " all operating systems. Cabal 1.4 and later automatically strip"
491 ++ " executables. Cabal also has a flag --disable-executable-stripping"
492 ++ " which is necessary when building packages for some Linux"
493 ++ " distributions and using '-optl-Wl,-s' prevents that from working."
494 ppExplanation (OptExts fieldName) =
495 "Instead of '" ++ fieldName ++ ": -fglasgow-exts' it is preferable to use "
496 ++ "the 'extensions' field."
497 ppExplanation (OptThreaded fieldName) =
498 "'" ++ fieldName ++ ": -threaded' has no effect for libraries. It should "
499 ++ "only be used for executables."
500 ppExplanation (OptRts fieldName) =
501 "'" ++ fieldName ++ ": -rtsopts' has no effect for libraries. It should "
502 ++ "only be used for executables."
503 ppExplanation (OptWithRts fieldName) =
504 "'" ++ fieldName ++ ": -with-rtsopts' has no effect for libraries. It "
505 ++ "should only be used for executables."
506 ppExplanation (COptONumber prefix label) =
507 "'" ++ prefix ++": -O[n]' is generally not needed. When building with "
508 ++ " optimisations Cabal automatically adds '-O2' for " ++ label
509 ++ " code. Setting it yourself interferes with the"
510 ++ " --disable-optimization flag."
511 ppExplanation (COptCPP opt) =
512 "'cpp-options: " ++ opt ++ "' is not a portable C-preprocessor flag."
513 ppExplanation (OptAlternatives badField goodField flags) =
514 "Instead of " ++ quote (badField ++ ": " ++ unwords badFlags)
515 ++ " use " ++ quote (goodField ++ ": " ++ unwords goodFlags)
516 where (badFlags, goodFlags) = unzip flags
517 ppExplanation (RelativeOutside field path) =
518 quote (field ++ ": " ++ path)
519 ++ " is a relative path outside of the source tree. "
520 ++ "This will not work when generating a tarball with 'sdist'."
521 ppExplanation (AbsolutePath field path) =
522 quote (field ++ ": " ++ path) ++ " specifies an absolute path, but the "
523 ++ quote field ++ " field must use relative paths."
524 ppExplanation (BadRelativePAth field path err) =
525 quote (field ++ ": " ++ path)
526 ++ " is not a good relative path: " ++ show err
527 ppExplanation (DistPoint mfield path) =
528 incipit ++ " points inside the 'dist' "
529 ++ "directory. This is not reliable because the location of this "
530 ++ "directory is configurable by the user (or package manager). In "
531 ++ "addition the layout of the 'dist' directory is subject to change "
532 ++ "in future versions of Cabal."
533 where -- mfiled Nothing -> the path is inside `ghc-options`
534 incipit = maybe ("'ghc-options' path " ++ quote path)
535 (\field -> quote (field ++ ": " ++ path))
536 mfield
537 ppExplanation (GlobSyntaxError field expl) =
538 "In the '" ++ field ++ "' field: " ++ expl
539 ppExplanation (InvalidOnWin paths) =
540 "The " ++ quotes paths ++ " invalid on Windows, which "
541 ++ "would cause portability problems for this package. Windows file "
542 ++ "names cannot contain any of the characters \":*?<>|\" and there "
543 ++ "a few reserved names including \"aux\", \"nul\", \"con\", "
544 ++ "\"prn\", \"com1-9\", \"lpt1-9\" and \"clock$\"."
545 where quotes [failed] = "path " ++ quote failed ++ " is"
546 quotes failed = "paths " ++ intercalate ", " (map quote failed)
547 ++ " are"
548 ppExplanation (FilePathTooLong path) =
549 "The following file name is too long to store in a portable POSIX "
550 ++ "format tar archive. The maximum length is 255 ASCII characters.\n"
551 ++ "The file in question is:\n " ++ path
552 ppExplanation (FilePathNameTooLong path) =
553 "The following file name is too long to store in a portable POSIX "
554 ++ "format tar archive. The maximum length for the name part (including "
555 ++ "extension) is 100 ASCII characters. The maximum length for any "
556 ++ "individual directory component is 155.\n"
557 ++ "The file in question is:\n " ++ path
558 ppExplanation (FilePathSplitTooLong path) =
559 "The following file name is too long to store in a portable POSIX "
560 ++ "format tar archive. While the total length is less than 255 ASCII "
561 ++ "characters, there are unfortunately further restrictions. It has to "
562 ++ "be possible to split the file path on a directory separator into "
563 ++ "two parts such that the first part fits in 155 characters or less "
564 ++ "and the second part fits in 100 characters or less. Basically you "
565 ++ "have to make the file name or directory names shorter, or you could "
566 ++ "split a long directory name into nested subdirectories with shorter "
567 ++ "names.\nThe file in question is:\n " ++ path
568 ppExplanation FilePathEmpty =
569 "Encountered a file with an empty name, something is very wrong! "
570 ++ "Files with an empty name cannot be stored in a tar archive or in "
571 ++ "standard file systems."
572 ppExplanation CVTestSuite =
573 "The 'test-suite' section is new in Cabal 1.10. "
574 ++ "Unfortunately it messes up the parser in older Cabal versions "
575 ++ "so you must specify at least 'cabal-version: >= 1.8', but note "
576 ++ "that only Cabal 1.10 and later can actually run such test suites."
577 ppExplanation CVDefaultLanguage =
578 "To use the 'default-language' field the package needs to specify "
579 ++ "at least 'cabal-version: >= 1.10'."
580 ppExplanation CVDefaultLanguageComponent =
581 "Packages using 'cabal-version: >= 1.10' and before 'cabal-version: 3.4' "
582 ++ "must specify the 'default-language' field for each component (e.g. "
583 ++ "Haskell98 or Haskell2010). If a component uses different languages "
584 ++ "in different modules then list the other ones in the "
585 ++ "'other-languages' field."
586 ppExplanation CVExtraDocFiles =
587 "To use the 'extra-doc-files' field the package needs to specify "
588 ++ "'cabal-version: 1.18' or higher."
589 ppExplanation CVMultiLib =
590 "To use multiple 'library' sections or a named library section "
591 ++ "the package needs to specify at least 'cabal-version: 2.0'."
592 ppExplanation CVReexported =
593 "To use the 'reexported-module' field the package needs to specify "
594 ++ "'cabal-version: 1.22' or higher."
595 ppExplanation CVMixins =
596 "To use the 'mixins' field the package needs to specify "
597 ++ "at least 'cabal-version: 2.0'."
598 ppExplanation CVExtraFrameworkDirs =
599 "To use the 'extra-framework-dirs' field the package needs to specify"
600 ++ " 'cabal-version: 1.24' or higher."
601 ppExplanation CVDefaultExtensions =
602 "To use the 'default-extensions' field the package needs to specify "
603 ++ "at least 'cabal-version: >= 1.10'."
604 ppExplanation CVExtensionsDeprecated =
605 "For packages using 'cabal-version: >= 1.10' the 'extensions' "
606 ++ "field is deprecated. The new 'default-extensions' field lists "
607 ++ "extensions that are used in all modules in the component, while "
608 ++ "the 'other-extensions' field lists extensions that are used in "
609 ++ "some modules, e.g. via the {-# LANGUAGE #-} pragma."
610 ppExplanation CVSources =
611 "The use of 'asm-sources', 'cmm-sources', 'extra-bundled-libraries' "
612 ++ " and 'extra-library-flavours' requires the package "
613 ++ " to specify at least 'cabal-version: 3.0'."
614 ppExplanation (CVExtraDynamic flavs) =
615 "The use of 'extra-dynamic-library-flavours' requires the package "
616 ++ " to specify at least 'cabal-version: 3.0'. The flavours are: "
617 ++ commaSep (concat flavs)
618 ppExplanation CVVirtualModules =
619 "The use of 'virtual-modules' requires the package "
620 ++ " to specify at least 'cabal-version: 2.2'."
621 ppExplanation CVSourceRepository =
622 "The 'source-repository' section is new in Cabal 1.6. "
623 ++ "Unfortunately it messes up the parser in earlier Cabal versions "
624 ++ "so you need to specify 'cabal-version: >= 1.6'."
625 ppExplanation (CVExtensions version extCab12) =
626 "Unfortunately the language extensions "
627 ++ commaSep (map (quote . prettyShow) extCab12)
628 ++ " break the parser in earlier Cabal versions so you need to "
629 ++ "specify 'cabal-version: >= " ++ showCabalSpecVersion version
630 ++ "'. Alternatively if you require compatibility with earlier "
631 ++ "Cabal versions then you may be able to use an equivalent "
632 ++ "compiler-specific flag."
633 ppExplanation CVCustomSetup =
634 "Packages using 'cabal-version: 1.24' or higher with 'build-type: Custom' "
635 ++ "must use a 'custom-setup' section with a 'setup-depends' field "
636 ++ "that specifies the dependencies of the Setup.hs script itself. "
637 ++ "The 'setup-depends' field uses the same syntax as 'build-depends', "
638 ++ "so a simple example would be 'setup-depends: base, Cabal'."
639 ppExplanation CVExpliticDepsCustomSetup =
640 "From version 1.24 cabal supports specifying explicit dependencies "
641 ++ "for Custom setup scripts. Consider using 'cabal-version: 1.24' or "
642 ++ "higher and adding a 'custom-setup' section with a 'setup-depends' "
643 ++ "field that specifies the dependencies of the Setup.hs script "
644 ++ "itself. The 'setup-depends' field uses the same syntax as "
645 ++ "'build-depends', so a simple example would be 'setup-depends: base, "
646 ++ "Cabal'."
647 ppExplanation CVAutogenPaths =
648 "Packages using 'cabal-version: 2.0' and the autogenerated "
649 ++ "module Paths_* must include it also on the 'autogen-modules' field "
650 ++ "besides 'exposed-modules' and 'other-modules'. This specifies that "
651 ++ "the module does not come with the package and is generated on "
652 ++ "setup. Modules built with a custom Setup.hs script also go here "
653 ++ "to ensure that commands like sdist don't fail."
654 ppExplanation (GlobNoMatch field glob) =
655 "In '" ++ field ++ "': the pattern '" ++ glob ++ "' does not"
656 ++ " match any files."
657 ppExplanation (GlobExactMatch field glob file) =
658 "In '" ++ field ++ "': the pattern '" ++ glob ++ "' does not"
659 ++ " match the file '" ++ file ++ "' because the extensions do not"
660 ++ " exactly match (e.g., foo.en.html does not exactly match *.html)."
661 ++ " To enable looser suffix-only matching, set 'cabal-version: 2.4' or"
662 ++ " higher."
663 ppExplanation (GlobNoDir field glob dir) =
664 "In '" ++ field ++ "': the pattern '" ++ glob ++ "' attempts to"
665 ++ " match files in the directory '" ++ dir ++ "', but there is no"
666 ++ " directory by that name."
667 ppExplanation (UnknownOS unknownOSs) =
668 "Unknown operating system name " ++ commaSep (map quote unknownOSs)
669 ppExplanation (UnknownArch unknownArches) =
670 "Unknown architecture name " ++ commaSep (map quote unknownArches)
671 ppExplanation (UnknownCompiler unknownImpls) =
672 "Unknown compiler name " ++ commaSep (map quote unknownImpls)
673 ppExplanation BaseNoUpperBounds =
674 "The dependency 'build-depends: base' does not specify an upper "
675 ++ "bound on the version number. Each major release of the 'base' "
676 ++ "package changes the API in various ways and most packages will "
677 ++ "need some changes to compile with it. The recommended practice "
678 ++ "is to specify an upper bound on the version of the 'base' "
679 ++ "package. This ensures your package will continue to build when a "
680 ++ "new major version of the 'base' package is released. If you are "
681 ++ "not sure what upper bound to use then use the next major "
682 ++ "version. For example if you have tested your package with 'base' "
683 ++ "version 4.5 and 4.6 then use 'build-depends: base >= 4.5 && < 4.7'."
684 ppExplanation (SuspiciousFlagName invalidFlagNames) =
685 "Suspicious flag names: " ++ unwords invalidFlagNames ++ ". "
686 ++ "To avoid ambiguity in command line interfaces, flag shouldn't "
687 ++ "start with a dash. Also for better compatibility, flag names "
688 ++ "shouldn't contain non-ascii characters."
689 ppExplanation (DeclaredUsedFlags declared used) =
690 "Declared and used flag sets differ: "
691 ++ s declared ++ " /= " ++ s used ++ ". "
692 where s :: Set.Set FlagName -> String
693 s = commaSep . map unFlagName . Set.toList
694 ppExplanation (NonASCIICustomField nonAsciiXFields) =
695 "Non ascii custom fields: " ++ unwords nonAsciiXFields ++ ". "
696 ++ "For better compatibility, custom field names "
697 ++ "shouldn't contain non-ascii characters."
698 ppExplanation RebindableClash =
699 "Packages using RebindableSyntax with OverloadedStrings or"
700 ++ " OverloadedLists in default-extensions, in conjunction with the"
701 ++ " autogenerated module Paths_*, are known to cause compile failures"
702 ++ " with Cabal < 2.2. To use these default-extensions with a Paths_*"
703 ++ " autogen module, specify at least 'cabal-version: 2.2'."
704 ppExplanation (WErrorUnneeded fieldName) = addConditionalExp $
705 "'" ++ fieldName ++ ": -Werror' makes the package easy to "
706 ++ "break with future GHC versions because new GHC versions often "
707 ++ "add new warnings."
708 ppExplanation (JUnneeded fieldName) = addConditionalExp $
709 "'" ++ fieldName ++ ": -j[N]' can make sense for specific user's setup,"
710 ++ " but it is not appropriate for a distributed package."
711 ppExplanation (FDeferTypeErrorsUnneeded fieldName) = addConditionalExp $
712 "'" ++ fieldName ++ ": -fdefer-type-errors' is fine during development "
713 ++ "but is not appropriate for a distributed package."
714 ppExplanation (DynamicUnneeded fieldName) = addConditionalExp $
715 "'" ++ fieldName ++ ": -d*' debug flags are not appropriate "
716 ++ "for a distributed package."
717 ppExplanation (ProfilingUnneeded fieldName) = addConditionalExp $
718 "'" ++ fieldName ++ ": -fprof*' profiling flags are typically not "
719 ++ "appropriate for a distributed library package. These flags are "
720 ++ "useful to profile this package, but when profiling other packages "
721 ++ "that use this one these flags clutter the profile output with "
722 ++ "excessive detail. If you think other packages really want to see "
723 ++ "cost centres from this package then use '-fprof-auto-exported' "
724 ++ "which puts cost centres only on exported functions."
725 ppExplanation (UpperBoundSetup nm) =
726 "The dependency 'setup-depends: '"++nm++"' does not specify an "
727 ++ "upper bound on the version number. Each major release of the "
728 ++ "'"++nm++"' package changes the API in various ways and most "
729 ++ "packages will need some changes to compile with it. If you are "
730 ++ "not sure what upper bound to use then use the next major "
731 ++ "version."
732 ppExplanation (DuplicateModule s dupLibsLax) =
733 "Duplicate modules in " ++ s ++ ": "
734 ++ commaSep (map prettyShow dupLibsLax)
735 ppExplanation (PotentialDupModule s dupLibsStrict) =
736 "Potential duplicate modules (subject to conditionals) in " ++ s
737 ++ ": " ++ commaSep (map prettyShow dupLibsStrict)
738 ppExplanation (BOMStart pdfile) =
739 pdfile ++ " starts with an Unicode byte order mark (BOM)."
740 ++ " This may cause problems with older cabal versions."
741 ppExplanation (NotPackageName pdfile expectedCabalname) =
742 "The filename " ++ quote pdfile ++ " does not match package name "
743 ++ "(expected: " ++ quote expectedCabalname ++ ")"
744 ppExplanation NoDesc =
745 "No cabal file found.\n"
746 ++ "Please create a package description file <pkgname>.cabal"
747 ppExplanation (MultiDesc multiple) =
748 "Multiple cabal files found while checking.\n"
749 ++ "Please use only one of: "
750 ++ intercalate ", " multiple
751 ppExplanation (UnknownFile fieldname file) =
752 "The '" ++ fieldname ++ "' field refers to the file "
753 ++ quote (getSymbolicPath file) ++ " which does not exist."
754 ppExplanation MissingSetupFile =
755 "The package is missing a Setup.hs or Setup.lhs script."
756 ppExplanation MissingConfigureScript =
757 "The 'build-type' is 'Configure' but there is no 'configure' script. "
758 ++ "You probably need to run 'autoreconf -i' to generate it."
759 ppExplanation (UnknownDirectory kind dir) =
760 quote (kind ++ ": " ++ dir)
761 ++ " specifies a directory which does not exist."
762 ppExplanation MissingSourceControl =
763 "When distributing packages it is encouraged to specify source "
764 ++ "control information in the .cabal file using one or more "
765 ++ "'source-repository' sections. See the Cabal user guide for "
766 ++ "details."
769 -- | Results of some kind of failed package check.
771 -- There are a range of severities, from merely dubious to totally insane.
772 -- All of them come with a human readable explanation. In future we may augment
773 -- them with more machine readable explanations, for example to help an IDE
774 -- suggest automatic corrections.
776 data PackageCheck =
778 -- | This package description is no good. There's no way it's going to
779 -- build sensibly. This should give an error at configure time.
780 PackageBuildImpossible { explanation :: CheckExplanation }
782 -- | A problem that is likely to affect building the package, or an
783 -- issue that we'd like every package author to be aware of, even if
784 -- the package is never distributed.
785 | PackageBuildWarning { explanation :: CheckExplanation }
787 -- | An issue that might not be a problem for the package author but
788 -- might be annoying or detrimental when the package is distributed to
789 -- users. We should encourage distributed packages to be free from these
790 -- issues, but occasionally there are justifiable reasons so we cannot
791 -- ban them entirely.
792 | PackageDistSuspicious { explanation :: CheckExplanation }
794 -- | Like PackageDistSuspicious but will only display warnings
795 -- rather than causing abnormal exit when you run 'cabal check'.
796 | PackageDistSuspiciousWarn { explanation :: CheckExplanation }
798 -- | An issue that is OK in the author's environment but is almost
799 -- certain to be a portability problem for other environments. We can
800 -- quite legitimately refuse to publicly distribute packages with these
801 -- problems.
802 | PackageDistInexcusable { explanation :: CheckExplanation }
803 deriving (Eq, Ord)
805 -- | Pretty printing 'PackageCheck'.
807 ppPackageCheck :: PackageCheck -> String
808 ppPackageCheck e = ppExplanation (explanation e)
810 instance Show PackageCheck where
811 show notice = ppPackageCheck notice
813 check :: Bool -> PackageCheck -> Maybe PackageCheck
814 check False _ = Nothing
815 check True pc = Just pc
817 checkSpecVersion :: PackageDescription -> CabalSpecVersion -> Bool -> PackageCheck
818 -> Maybe PackageCheck
819 checkSpecVersion pkg specver cond pc
820 | specVersion pkg >= specver = Nothing
821 | otherwise = check cond pc
823 -- ------------------------------------------------------------
824 -- * Standard checks
825 -- ------------------------------------------------------------
827 -- | Check for common mistakes and problems in package descriptions.
829 -- This is the standard collection of checks covering all aspects except
830 -- for checks that require looking at files within the package. For those
831 -- see 'checkPackageFiles'.
833 -- It requires the 'GenericPackageDescription' and optionally a particular
834 -- configuration of that package. If you pass 'Nothing' then we just check
835 -- a version of the generic description using 'flattenPackageDescription'.
837 checkPackage :: GenericPackageDescription
838 -> Maybe PackageDescription
839 -> [PackageCheck]
840 checkPackage gpkg mpkg =
841 checkConfiguredPackage pkg
842 ++ checkConditionals gpkg
843 ++ checkPackageVersions gpkg
844 ++ checkDevelopmentOnlyFlags gpkg
845 ++ checkFlagNames gpkg
846 ++ checkUnusedFlags gpkg
847 ++ checkUnicodeXFields gpkg
848 ++ checkPathsModuleExtensions pkg
849 ++ checkSetupVersions gpkg
850 ++ checkDuplicateModules gpkg
851 where
852 pkg = fromMaybe (flattenPackageDescription gpkg) mpkg
854 --TODO: make this variant go away
855 -- we should always know the GenericPackageDescription
856 checkConfiguredPackage :: PackageDescription -> [PackageCheck]
857 checkConfiguredPackage pkg =
858 checkSanity pkg
859 ++ checkFields pkg
860 ++ checkLicense pkg
861 ++ checkSourceRepos pkg
862 ++ checkAllGhcOptions pkg
863 ++ checkCCOptions pkg
864 ++ checkCxxOptions pkg
865 ++ checkCPPOptions pkg
866 ++ checkPaths pkg
867 ++ checkCabalVersion pkg
870 -- ------------------------------------------------------------
871 -- * Basic sanity checks
872 -- ------------------------------------------------------------
874 -- | Check that this package description is sane.
876 checkSanity :: PackageDescription -> [PackageCheck]
877 checkSanity pkg =
878 catMaybes [
880 check (null . unPackageName . packageName $ pkg) $
881 PackageBuildImpossible NoNameField
883 , check (nullVersion == packageVersion pkg) $
884 PackageBuildImpossible NoVersionField
886 , check (all ($ pkg) [ null . executables
887 , null . testSuites
888 , null . benchmarks
889 , null . allLibraries
890 , null . foreignLibs ]) $
891 PackageBuildImpossible NoTarget
893 , check (any (== LMainLibName) (map libName $ subLibraries pkg)) $
894 PackageBuildImpossible UnnamedInternal
896 , check (not (null duplicateNames)) $
897 PackageBuildImpossible (DuplicateSections duplicateNames)
899 -- NB: but it's OK for executables to have the same name!
900 -- TODO shouldn't need to compare on the string level
901 , check (any (== prettyShow (packageName pkg))
902 (prettyShow <$> subLibNames)) $
903 PackageBuildImpossible (IllegalLibraryName pkg)
905 --TODO: check for name clashes case insensitively: windows file systems cannot
906 --cope.
908 ++ concatMap (checkLibrary pkg) (allLibraries pkg)
909 ++ concatMap (checkExecutable pkg) (executables pkg)
910 ++ concatMap (checkTestSuite pkg) (testSuites pkg)
911 ++ concatMap (checkBenchmark pkg) (benchmarks pkg)
913 where
914 -- The public 'library' gets special dispensation, because it
915 -- is common practice to export a library and name the executable
916 -- the same as the package.
917 subLibNames = mapMaybe (libraryNameString . libName) $ subLibraries pkg
918 exeNames = map exeName $ executables pkg
919 testNames = map testName $ testSuites pkg
920 bmNames = map benchmarkName $ benchmarks pkg
921 duplicateNames = dups $ subLibNames ++ exeNames ++ testNames ++ bmNames
923 checkLibrary :: PackageDescription -> Library -> [PackageCheck]
924 checkLibrary pkg lib =
925 catMaybes [
927 -- TODO: This check is bogus if a required-signature was passed through
928 check (null (explicitLibModules lib) && null (reexportedModules lib)) $
929 PackageDistSuspiciousWarn (NoModulesExposed lib)
931 -- check use of signatures sections
932 , checkVersion CabalSpecV2_0 (not (null (signatures lib))) $
933 PackageDistInexcusable SignaturesCabal2
935 -- check that all autogen-modules appear on other-modules or exposed-modules
936 , check
937 (not $ and $ map (flip elem (explicitLibModules lib)) (libModulesAutogen lib)) $
938 PackageBuildImpossible AutogenNotExposed
940 -- check that all autogen-includes appear on includes or install-includes
941 , check
942 (not $ and $ map (flip elem (allExplicitIncludes lib)) (view L.autogenIncludes lib)) $
943 PackageBuildImpossible AutogenIncludesNotIncluded
946 where
947 checkVersion :: CabalSpecVersion -> Bool -> PackageCheck -> Maybe PackageCheck
948 checkVersion ver cond pc
949 | specVersion pkg >= ver = Nothing
950 | otherwise = check cond pc
952 allExplicitIncludes :: L.HasBuildInfo a => a -> [FilePath]
953 allExplicitIncludes x = view L.includes x ++ view L.installIncludes x
955 checkExecutable :: PackageDescription -> Executable -> [PackageCheck]
956 checkExecutable pkg exe =
957 catMaybes [
959 check (null (modulePath exe)) $
960 PackageBuildImpossible (NoMainIs exe)
962 -- This check does not apply to scripts.
963 , check (package pkg /= fakePackageId
964 && not (null (modulePath exe))
965 && not (fileExtensionSupportedLanguage $ modulePath exe)) $
966 PackageBuildImpossible NoHsLhsMain
968 , checkSpecVersion pkg CabalSpecV1_18
969 (fileExtensionSupportedLanguage (modulePath exe)
970 && takeExtension (modulePath exe) `notElem` [".hs", ".lhs"]) $
971 PackageDistInexcusable MainCCabal1_18
973 -- check that all autogen-modules appear on other-modules
974 , check
975 (not $ and $ map (flip elem (exeModules exe)) (exeModulesAutogen exe)) $
976 PackageBuildImpossible (AutogenNoOther CETExecutable (exeName exe))
978 -- check that all autogen-includes appear on includes
979 , check
980 (not $ and $ map (flip elem (view L.includes exe)) (view L.autogenIncludes exe)) $
981 PackageBuildImpossible AutogenIncludesNotIncludedExe
984 checkTestSuite :: PackageDescription -> TestSuite -> [PackageCheck]
985 checkTestSuite pkg test =
986 catMaybes [
988 case testInterface test of
989 TestSuiteUnsupported tt@(TestTypeUnknown _ _) -> Just $
990 PackageBuildWarning (TestsuiteTypeNotKnown tt)
992 TestSuiteUnsupported tt -> Just $
993 PackageBuildWarning (TestsuiteNotSupported tt)
994 _ -> Nothing
996 , check mainIsWrongExt $
997 PackageBuildImpossible NoHsLhsMain
999 , checkSpecVersion pkg CabalSpecV1_18 (mainIsNotHsExt && not mainIsWrongExt) $
1000 PackageDistInexcusable MainCCabal1_18
1002 -- check that all autogen-modules appear on other-modules
1003 , check
1004 (not $ and $ map (flip elem (testModules test)) (testModulesAutogen test)) $
1005 PackageBuildImpossible (AutogenNoOther CETTest (testName test))
1007 -- check that all autogen-includes appear on includes
1008 , check
1009 (not $ and $ map (flip elem (view L.includes test)) (view L.autogenIncludes test)) $
1010 PackageBuildImpossible AutogenIncludesNotIncludedExe
1012 where
1013 mainIsWrongExt = case testInterface test of
1014 TestSuiteExeV10 _ f -> not $ fileExtensionSupportedLanguage f
1015 _ -> False
1017 mainIsNotHsExt = case testInterface test of
1018 TestSuiteExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]
1019 _ -> False
1021 checkBenchmark :: PackageDescription -> Benchmark -> [PackageCheck]
1022 checkBenchmark _pkg bm =
1023 catMaybes [
1025 case benchmarkInterface bm of
1026 BenchmarkUnsupported tt@(BenchmarkTypeUnknown _ _) -> Just $
1027 PackageBuildWarning (BenchmarkTypeNotKnown tt)
1029 BenchmarkUnsupported tt -> Just $
1030 PackageBuildWarning (BenchmarkNotSupported tt)
1031 _ -> Nothing
1033 , check mainIsWrongExt $
1034 PackageBuildImpossible NoHsLhsMainBench
1036 -- check that all autogen-modules appear on other-modules
1037 , check
1038 (not $ and $ map (flip elem (benchmarkModules bm)) (benchmarkModulesAutogen bm)) $
1039 PackageBuildImpossible (AutogenNoOther CETBenchmark (benchmarkName bm))
1041 -- check that all autogen-includes appear on includes
1042 , check
1043 (not $ and $ map (flip elem (view L.includes bm)) (view L.autogenIncludes bm)) $
1044 PackageBuildImpossible AutogenIncludesNotIncludedExe
1046 where
1047 mainIsWrongExt = case benchmarkInterface bm of
1048 BenchmarkExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]
1049 _ -> False
1051 -- ------------------------------------------------------------
1052 -- * Additional pure checks
1053 -- ------------------------------------------------------------
1055 checkFields :: PackageDescription -> [PackageCheck]
1056 checkFields pkg =
1057 catMaybes [
1059 check (not . FilePath.Windows.isValid . prettyShow . packageName $ pkg) $
1060 PackageDistInexcusable (InvalidNameWin pkg)
1062 , check (isPrefixOf "z-" . prettyShow . packageName $ pkg) $
1063 PackageDistInexcusable ZPrefix
1065 , check (isNothing (buildTypeRaw pkg) && specVersion pkg < CabalSpecV2_2) $
1066 PackageBuildWarning NoBuildType
1068 , check (isJust (setupBuildInfo pkg) && buildType pkg /= Custom) $
1069 PackageBuildWarning NoCustomSetup
1071 , check (not (null unknownCompilers)) $
1072 PackageBuildWarning (UnknownCompilers unknownCompilers)
1074 , check (not (null unknownLanguages)) $
1075 PackageBuildWarning (UnknownLanguages unknownLanguages)
1077 , check (not (null unknownExtensions)) $
1078 PackageBuildWarning (UnknownExtensions unknownExtensions)
1080 , check (not (null languagesUsedAsExtensions)) $
1081 PackageBuildWarning (LanguagesAsExtension languagesUsedAsExtensions)
1083 , check (not (null ourDeprecatedExtensions)) $
1084 PackageDistSuspicious (DeprecatedExtensions ourDeprecatedExtensions)
1086 , check (ShortText.null (category pkg)) $
1087 PackageDistSuspicious (MissingField CEFCategory)
1089 , check (ShortText.null (maintainer pkg)) $
1090 PackageDistSuspicious (MissingField CEFMaintainer)
1092 , check (ShortText.null (synopsis pkg) && ShortText.null (description pkg)) $
1093 PackageDistInexcusable (MissingField CEFSynOrDesc)
1095 , check (ShortText.null (description pkg) && not (ShortText.null (synopsis pkg))) $
1096 PackageDistSuspicious (MissingField CEFDescription)
1098 , check (ShortText.null (synopsis pkg) && not (ShortText.null (description pkg))) $
1099 PackageDistSuspicious (MissingField CEFSynopsis)
1101 --TODO: recommend the bug reports URL, author and homepage fields
1102 --TODO: recommend not using the stability field
1103 --TODO: recommend specifying a source repo
1105 , check (ShortText.length (synopsis pkg) > 80) $
1106 PackageDistSuspicious SynopsisTooLong
1108 -- See also https://github.com/haskell/cabal/pull/3479
1109 , check (not (ShortText.null (description pkg))
1110 && ShortText.length (description pkg) <= ShortText.length (synopsis pkg)) $
1111 PackageDistSuspicious ShortDesc
1113 -- check use of impossible constraints "tested-with: GHC== 6.10 && ==6.12"
1114 , check (not (null testedWithImpossibleRanges)) $
1115 PackageDistInexcusable (InvalidTestWith testedWithImpossibleRanges)
1117 -- for more details on why the following was commented out,
1118 -- check https://github.com/haskell/cabal/pull/7470#issuecomment-875878507
1119 -- , check (not (null depInternalLibraryWithExtraVersion)) $
1120 -- PackageBuildWarning $
1121 -- "The package has an extraneous version range for a dependency on an "
1122 -- ++ "internal library: "
1123 -- ++ commaSep (map prettyShow depInternalLibraryWithExtraVersion)
1124 -- ++ ". This version range includes the current package but isn't needed "
1125 -- ++ "as the current package's library will always be used."
1127 , check (not (null depInternalLibraryWithImpossibleVersion)) $
1128 PackageBuildImpossible
1129 (ImpossibleInternalDep depInternalLibraryWithImpossibleVersion)
1131 -- , check (not (null depInternalExecutableWithExtraVersion)) $
1132 -- PackageBuildWarning $
1133 -- "The package has an extraneous version range for a dependency on an "
1134 -- ++ "internal executable: "
1135 -- ++ commaSep (map prettyShow depInternalExecutableWithExtraVersion)
1136 -- ++ ". This version range includes the current package but isn't needed "
1137 -- ++ "as the current package's executable will always be used."
1139 , check (not (null depInternalExecutableWithImpossibleVersion)) $
1140 PackageBuildImpossible
1141 (ImpossibleInternalExe depInternalExecutableWithImpossibleVersion)
1143 , check (not (null depMissingInternalExecutable)) $
1144 PackageBuildImpossible (MissingInternalExe depMissingInternalExecutable)
1146 where
1147 unknownCompilers = [ name | (OtherCompiler name, _) <- testedWith pkg ]
1148 unknownLanguages = [ name | bi <- allBuildInfo pkg
1149 , UnknownLanguage name <- allLanguages bi ]
1150 unknownExtensions = [ name | bi <- allBuildInfo pkg
1151 , UnknownExtension name <- allExtensions bi
1152 , name `notElem` map prettyShow knownLanguages ]
1153 ourDeprecatedExtensions = nub $ catMaybes
1154 [ find ((==ext) . fst) deprecatedExtensions
1155 | bi <- allBuildInfo pkg
1156 , ext <- allExtensions bi ]
1157 languagesUsedAsExtensions =
1158 [ name | bi <- allBuildInfo pkg
1159 , UnknownExtension name <- allExtensions bi
1160 , name `elem` map prettyShow knownLanguages ]
1162 testedWithImpossibleRanges =
1163 [ Dependency (mkPackageName (prettyShow compiler)) vr mainLibSet
1164 | (compiler, vr) <- testedWith pkg
1165 , isNoVersion vr ]
1167 internalLibraries =
1168 map (maybe (packageName pkg) unqualComponentNameToPackageName . libraryNameString . libName)
1169 (allLibraries pkg)
1171 internalExecutables = map exeName $ executables pkg
1173 internalLibDeps =
1174 [ dep
1175 | bi <- allBuildInfo pkg
1176 , dep@(Dependency name _ _) <- targetBuildDepends bi
1177 , name `elem` internalLibraries
1180 internalExeDeps =
1181 [ dep
1182 | bi <- allBuildInfo pkg
1183 , dep <- getAllToolDependencies pkg bi
1184 , isInternal pkg dep
1187 -- depInternalLibraryWithExtraVersion =
1188 -- [ dep
1189 -- | dep@(Dependency _ versionRange _) <- internalLibDeps
1190 -- , not $ isAnyVersion versionRange
1191 -- , packageVersion pkg `withinRange` versionRange
1192 -- ]
1194 depInternalLibraryWithImpossibleVersion =
1195 [ dep
1196 | dep@(Dependency _ versionRange _) <- internalLibDeps
1197 , not $ packageVersion pkg `withinRange` versionRange
1200 -- depInternalExecutableWithExtraVersion =
1201 -- [ dep
1202 -- | dep@(ExeDependency _ _ versionRange) <- internalExeDeps
1203 -- , not $ isAnyVersion versionRange
1204 -- , packageVersion pkg `withinRange` versionRange
1205 -- ]
1207 depInternalExecutableWithImpossibleVersion =
1208 [ dep
1209 | dep@(ExeDependency _ _ versionRange) <- internalExeDeps
1210 , not $ packageVersion pkg `withinRange` versionRange
1213 depMissingInternalExecutable =
1214 [ dep
1215 | dep@(ExeDependency _ eName _) <- internalExeDeps
1216 , not $ eName `elem` internalExecutables
1219 checkLicense :: PackageDescription -> [PackageCheck]
1220 checkLicense pkg = case licenseRaw pkg of
1221 Right l -> checkOldLicense pkg l
1222 Left l -> checkNewLicense pkg l
1224 checkNewLicense :: PackageDescription -> SPDX.License -> [PackageCheck]
1225 checkNewLicense _pkg lic = catMaybes
1226 [ check (lic == SPDX.NONE) $
1227 PackageDistInexcusable NONELicense ]
1229 checkOldLicense :: PackageDescription -> License -> [PackageCheck]
1230 checkOldLicense pkg lic = catMaybes
1231 [ check (lic == UnspecifiedLicense) $
1232 PackageDistInexcusable NoLicense
1234 , check (lic == AllRightsReserved) $
1235 PackageDistSuspicious AllRightsReservedLicense
1237 , checkVersion CabalSpecV1_4 (lic `notElem` compatLicenses) $
1238 PackageDistInexcusable (LicenseMessParse pkg)
1240 , case lic of
1241 UnknownLicense l -> Just $ PackageBuildWarning (UnrecognisedLicense l)
1242 _ -> Nothing
1244 , check (lic == BSD4) $
1245 PackageDistSuspicious UncommonBSD4
1247 , case unknownLicenseVersion lic of
1248 Just knownVersions -> Just $
1249 PackageDistSuspicious (UnknownLicenseVersion lic knownVersions)
1250 _ -> Nothing
1252 , check (lic `notElem` [ AllRightsReserved
1253 , UnspecifiedLicense, PublicDomain]
1254 -- AllRightsReserved and PublicDomain are not strictly
1255 -- licenses so don't need license files.
1256 && null (licenseFiles pkg)) $
1257 PackageDistSuspicious NoLicenseFile
1259 where
1260 unknownLicenseVersion (GPL (Just v))
1261 | v `notElem` knownVersions = Just knownVersions
1262 where knownVersions = [ v' | GPL (Just v') <- knownLicenses ]
1263 unknownLicenseVersion (LGPL (Just v))
1264 | v `notElem` knownVersions = Just knownVersions
1265 where knownVersions = [ v' | LGPL (Just v') <- knownLicenses ]
1266 unknownLicenseVersion (AGPL (Just v))
1267 | v `notElem` knownVersions = Just knownVersions
1268 where knownVersions = [ v' | AGPL (Just v') <- knownLicenses ]
1269 unknownLicenseVersion (Apache (Just v))
1270 | v `notElem` knownVersions = Just knownVersions
1271 where knownVersions = [ v' | Apache (Just v') <- knownLicenses ]
1272 unknownLicenseVersion _ = Nothing
1274 checkVersion :: CabalSpecVersion -> Bool -> PackageCheck -> Maybe PackageCheck
1275 checkVersion ver cond pc
1276 | specVersion pkg >= ver = Nothing
1277 | otherwise = check cond pc
1279 compatLicenses = [ GPL Nothing, LGPL Nothing, AGPL Nothing, BSD3, BSD4
1280 , PublicDomain, AllRightsReserved
1281 , UnspecifiedLicense, OtherLicense ]
1283 checkSourceRepos :: PackageDescription -> [PackageCheck]
1284 checkSourceRepos pkg =
1285 catMaybes $ concat [[
1287 case repoKind repo of
1288 RepoKindUnknown kind -> Just $ PackageDistInexcusable $
1289 UnrecognisedSourceRepo kind
1290 _ -> Nothing
1292 , check (isNothing (repoType repo)) $
1293 PackageDistInexcusable MissingType
1295 , check (isNothing (repoLocation repo)) $
1296 PackageDistInexcusable MissingLocation
1298 , check (repoType repo == Just (KnownRepoType CVS) && isNothing (repoModule repo)) $
1299 PackageDistInexcusable MissingModule
1301 , check (repoKind repo == RepoThis && isNothing (repoTag repo)) $
1302 PackageDistInexcusable MissingTag
1304 , check (maybe False isAbsoluteOnAnyPlatform (repoSubdir repo)) $
1305 PackageDistInexcusable SubdirRelPath
1307 , do
1308 subdir <- repoSubdir repo
1309 err <- isGoodRelativeDirectoryPath subdir
1310 return $ PackageDistInexcusable (SubdirGoodRelPath err)
1312 | repo <- sourceRepos pkg ]
1314 --TODO: check location looks like a URL for some repo types.
1316 -- | Checks GHC options from all ghc-*-options fields in the given
1317 -- PackageDescription and reports commonly misused or non-portable flags
1318 checkAllGhcOptions :: PackageDescription -> [PackageCheck]
1319 checkAllGhcOptions pkg =
1320 checkGhcOptions "ghc-options" (hcOptions GHC) pkg
1321 ++ checkGhcOptions "ghc-prof-options" (hcProfOptions GHC) pkg
1322 ++ checkGhcOptions "ghc-shared-options" (hcSharedOptions GHC) pkg
1324 -- | Extracts GHC options belonging to the given field from the given
1325 -- PackageDescription using given function and checks them for commonly misused
1326 -- or non-portable flags
1327 checkGhcOptions :: String -> (BuildInfo -> [String]) -> PackageDescription -> [PackageCheck]
1328 checkGhcOptions fieldName getOptions pkg =
1329 catMaybes [
1331 checkFlags ["-fasm"] $
1332 PackageDistInexcusable (OptFasm fieldName)
1334 , checkFlags ["-fvia-C"] $
1335 PackageDistSuspicious (OptViaC fieldName)
1337 , checkFlags ["-fhpc"] $
1338 PackageDistInexcusable (OptHpc fieldName)
1340 , checkFlags ["-prof"] $
1341 PackageBuildWarning (OptProf fieldName)
1343 , checkFlags ["-o"] $
1344 PackageBuildWarning (OptO fieldName)
1346 , checkFlags ["-hide-package"] $
1347 PackageBuildWarning (OptHide fieldName)
1349 , checkFlags ["--make"] $
1350 PackageBuildWarning (OptMake fieldName)
1352 , checkFlags ["-main-is"] $
1353 PackageDistSuspicious (OptMain fieldName)
1355 , checkNonTestAndBenchmarkFlags ["-O0", "-Onot"] $
1356 PackageDistSuspicious (OptONot fieldName)
1358 , checkTestAndBenchmarkFlags ["-O0", "-Onot"] $
1359 PackageDistSuspiciousWarn (OptONot fieldName)
1361 , checkFlags [ "-O", "-O1"] $
1362 PackageDistInexcusable (OptOOne fieldName)
1364 , checkFlags ["-O2"] $
1365 PackageDistSuspiciousWarn (OptOTwo fieldName)
1367 , checkFlags ["-split-sections"] $
1368 PackageBuildWarning (OptSplitSections fieldName)
1370 , checkFlags ["-split-objs"] $
1371 PackageBuildWarning (OptSplitObjs fieldName)
1373 , checkFlags ["-optl-Wl,-s", "-optl-s"] $
1374 PackageDistInexcusable (OptWls fieldName)
1376 , checkFlags ["-fglasgow-exts"] $
1377 PackageDistSuspicious (OptExts fieldName)
1379 , check ("-threaded" `elem` lib_ghc_options) $
1380 PackageBuildWarning (OptThreaded fieldName)
1382 , check ("-rtsopts" `elem` lib_ghc_options) $
1383 PackageBuildWarning (OptRts fieldName)
1385 , check (any (\opt -> "-with-rtsopts" `isPrefixOf` opt) lib_ghc_options) $
1386 PackageBuildWarning (OptWithRts fieldName)
1388 , checkAlternatives fieldName "extensions"
1389 [ (flag, prettyShow extension) | flag <- ghc_options_no_rtsopts
1390 , Just extension <- [ghcExtension flag] ]
1392 , checkAlternatives fieldName "extensions"
1393 [ (flag, extension) | flag@('-':'X':extension) <- ghc_options_no_rtsopts ]
1395 , checkAlternatives fieldName "cpp-options" $
1396 [ (flag, flag) | flag@('-':'D':_) <- ghc_options_no_rtsopts ]
1397 ++ [ (flag, flag) | flag@('-':'U':_) <- ghc_options_no_rtsopts ]
1399 , checkAlternatives fieldName "include-dirs"
1400 [ (flag, dir) | flag@('-':'I':dir) <- ghc_options_no_rtsopts ]
1402 , checkAlternatives fieldName "extra-libraries"
1403 [ (flag, lib) | flag@('-':'l':lib) <- ghc_options_no_rtsopts ]
1405 , checkAlternatives fieldName "extra-libraries-static"
1406 [ (flag, lib) | flag@('-':'l':lib) <- ghc_options_no_rtsopts ]
1408 , checkAlternatives fieldName "extra-lib-dirs"
1409 [ (flag, dir) | flag@('-':'L':dir) <- ghc_options_no_rtsopts ]
1411 , checkAlternatives fieldName "extra-lib-dirs-static"
1412 [ (flag, dir) | flag@('-':'L':dir) <- ghc_options_no_rtsopts ]
1414 , checkAlternatives fieldName "frameworks"
1415 [ (flag, fmwk) | (flag@"-framework", fmwk) <-
1416 zip ghc_options_no_rtsopts (safeTail ghc_options_no_rtsopts) ]
1418 , checkAlternatives fieldName "extra-framework-dirs"
1419 [ (flag, dir) | (flag@"-framework-path", dir) <-
1420 zip ghc_options_no_rtsopts (safeTail ghc_options_no_rtsopts) ]
1423 where
1424 all_ghc_options = concatMap getOptions (allBuildInfo pkg)
1425 ghc_options_no_rtsopts = rmRtsOpts all_ghc_options
1426 lib_ghc_options = concatMap (getOptions . libBuildInfo)
1427 (allLibraries pkg)
1428 test_ghc_options = concatMap (getOptions . testBuildInfo)
1429 (testSuites pkg)
1430 benchmark_ghc_options = concatMap (getOptions . benchmarkBuildInfo)
1431 (benchmarks pkg)
1432 test_and_benchmark_ghc_options = test_ghc_options ++
1433 benchmark_ghc_options
1434 non_test_and_benchmark_ghc_options = concatMap getOptions
1435 (allBuildInfo (pkg { testSuites = []
1436 , benchmarks = []
1439 checkFlags :: [String] -> PackageCheck -> Maybe PackageCheck
1440 checkFlags flags = check (any (`elem` flags) all_ghc_options)
1442 checkTestAndBenchmarkFlags :: [String] -> PackageCheck -> Maybe PackageCheck
1443 checkTestAndBenchmarkFlags flags = check (any (`elem` flags) test_and_benchmark_ghc_options)
1445 checkNonTestAndBenchmarkFlags :: [String] -> PackageCheck -> Maybe PackageCheck
1446 checkNonTestAndBenchmarkFlags flags = check (any (`elem` flags) non_test_and_benchmark_ghc_options)
1448 ghcExtension ('-':'f':name) = case name of
1449 "allow-overlapping-instances" -> enable OverlappingInstances
1450 "no-allow-overlapping-instances" -> disable OverlappingInstances
1451 "th" -> enable TemplateHaskell
1452 "no-th" -> disable TemplateHaskell
1453 "ffi" -> enable ForeignFunctionInterface
1454 "no-ffi" -> disable ForeignFunctionInterface
1455 "fi" -> enable ForeignFunctionInterface
1456 "no-fi" -> disable ForeignFunctionInterface
1457 "monomorphism-restriction" -> enable MonomorphismRestriction
1458 "no-monomorphism-restriction" -> disable MonomorphismRestriction
1459 "mono-pat-binds" -> enable MonoPatBinds
1460 "no-mono-pat-binds" -> disable MonoPatBinds
1461 "allow-undecidable-instances" -> enable UndecidableInstances
1462 "no-allow-undecidable-instances" -> disable UndecidableInstances
1463 "allow-incoherent-instances" -> enable IncoherentInstances
1464 "no-allow-incoherent-instances" -> disable IncoherentInstances
1465 "arrows" -> enable Arrows
1466 "no-arrows" -> disable Arrows
1467 "generics" -> enable Generics
1468 "no-generics" -> disable Generics
1469 "implicit-prelude" -> enable ImplicitPrelude
1470 "no-implicit-prelude" -> disable ImplicitPrelude
1471 "implicit-params" -> enable ImplicitParams
1472 "no-implicit-params" -> disable ImplicitParams
1473 "bang-patterns" -> enable BangPatterns
1474 "no-bang-patterns" -> disable BangPatterns
1475 "scoped-type-variables" -> enable ScopedTypeVariables
1476 "no-scoped-type-variables" -> disable ScopedTypeVariables
1477 "extended-default-rules" -> enable ExtendedDefaultRules
1478 "no-extended-default-rules" -> disable ExtendedDefaultRules
1479 _ -> Nothing
1480 ghcExtension "-cpp" = enable CPP
1481 ghcExtension _ = Nothing
1483 enable e = Just (EnableExtension e)
1484 disable e = Just (DisableExtension e)
1486 rmRtsOpts :: [String] -> [String]
1487 rmRtsOpts ("-with-rtsopts":_:xs) = rmRtsOpts xs
1488 rmRtsOpts (x:xs) = x : rmRtsOpts xs
1489 rmRtsOpts [] = []
1492 checkCCOptions :: PackageDescription -> [PackageCheck]
1493 checkCCOptions = checkCLikeOptions "C" "cc-options" ccOptions
1495 checkCxxOptions :: PackageDescription -> [PackageCheck]
1496 checkCxxOptions = checkCLikeOptions "C++" "cxx-options" cxxOptions
1498 checkCLikeOptions :: String -> String -> (BuildInfo -> [String]) -> PackageDescription -> [PackageCheck]
1499 checkCLikeOptions label prefix accessor pkg =
1500 catMaybes [
1502 checkAlternatives prefix "include-dirs"
1503 [ (flag, dir) | flag@('-':'I':dir) <- all_cLikeOptions ]
1505 , checkAlternatives prefix "extra-libraries"
1506 [ (flag, lib) | flag@('-':'l':lib) <- all_cLikeOptions ]
1508 , checkAlternatives prefix "extra-lib-dirs"
1509 [ (flag, dir) | flag@('-':'L':dir) <- all_cLikeOptions ]
1511 , checkAlternatives "ld-options" "extra-libraries"
1512 [ (flag, lib) | flag@('-':'l':lib) <- all_ldOptions ]
1514 , checkAlternatives "ld-options" "extra-lib-dirs"
1515 [ (flag, dir) | flag@('-':'L':dir) <- all_ldOptions ]
1517 , checkCCFlags [ "-O", "-Os", "-O0", "-O1", "-O2", "-O3" ] $
1518 PackageDistSuspicious (COptONumber prefix label)
1521 where all_cLikeOptions = [ opts | bi <- allBuildInfo pkg
1522 , opts <- accessor bi ]
1523 all_ldOptions = [ opts | bi <- allBuildInfo pkg
1524 , opts <- ldOptions bi ]
1526 checkCCFlags :: [String] -> PackageCheck -> Maybe PackageCheck
1527 checkCCFlags flags = check (any (`elem` flags) all_cLikeOptions)
1529 checkCPPOptions :: PackageDescription -> [PackageCheck]
1530 checkCPPOptions pkg = catMaybes
1531 [ checkAlternatives "cpp-options" "include-dirs"
1532 [ (flag, dir) | flag@('-':'I':dir) <- all_cppOptions ]
1535 [ PackageBuildWarning (COptCPP opt)
1536 | opt <- all_cppOptions
1537 -- "-I" is handled above, we allow only -DNEWSTUFF and -UOLDSTUFF
1538 , not $ any (`isPrefixOf` opt) ["-D", "-U", "-I" ]
1540 where
1541 all_cppOptions = [ opts | bi <- allBuildInfo pkg, opts <- cppOptions bi ]
1543 checkAlternatives :: String -> String -> [(String, String)]
1544 -> Maybe PackageCheck
1545 checkAlternatives badField goodField flags =
1546 check (not (null badFlags)) $
1547 PackageBuildWarning (OptAlternatives badField goodField flags)
1548 where (badFlags, _) = unzip flags
1550 data PathKind
1551 = PathKindFile
1552 | PathKindDirectory
1553 | PathKindGlob
1554 deriving (Eq)
1556 checkPaths :: PackageDescription -> [PackageCheck]
1557 checkPaths pkg =
1558 checkPackageFileNamesWithGlob
1559 [ (kind == PathKindGlob, path)
1560 | (path, _, kind) <- relPaths ++ absPaths
1563 [ PackageBuildWarning (RelativeOutside field path)
1564 | (path, field, _) <- relPaths ++ absPaths
1565 , isOutsideTree path ]
1567 [ PackageDistInexcusable (AbsolutePath field path)
1568 | (path, field, _) <- relPaths
1569 , isAbsoluteOnAnyPlatform path ]
1571 [ PackageDistInexcusable (BadRelativePAth field path err)
1572 | (path, field, kind) <- relPaths
1573 -- these are not paths, but globs...
1574 , err <- maybeToList $ case kind of
1575 PathKindFile -> isGoodRelativeFilePath path
1576 PathKindGlob -> isGoodRelativeGlob path
1577 PathKindDirectory -> isGoodRelativeDirectoryPath path
1580 [ PackageDistInexcusable $ DistPoint (Just field) path
1581 | (path, field, _) <- relPaths ++ absPaths
1582 , isInsideDist path ]
1584 [ PackageDistInexcusable (DistPoint Nothing path)
1585 | bi <- allBuildInfo pkg
1586 , (GHC, flags) <- perCompilerFlavorToList $ options bi
1587 , path <- flags
1588 , isInsideDist path ]
1590 [ PackageDistInexcusable $
1591 GlobSyntaxError "data-files" (explainGlobSyntaxError pat err)
1592 | pat <- dataFiles pkg
1593 , Left err <- [parseFileGlob (specVersion pkg) pat]
1596 [ PackageDistInexcusable
1597 (GlobSyntaxError "extra-source-files" (explainGlobSyntaxError pat err))
1598 | pat <- extraSrcFiles pkg
1599 , Left err <- [parseFileGlob (specVersion pkg) pat]
1602 [ PackageDistInexcusable $
1603 GlobSyntaxError "extra-doc-files" (explainGlobSyntaxError pat err)
1604 | pat <- extraDocFiles pkg
1605 , Left err <- [parseFileGlob (specVersion pkg) pat]
1607 where
1608 isOutsideTree path = case splitDirectories path of
1609 "..":_ -> True
1610 ".":"..":_ -> True
1611 _ -> False
1612 isInsideDist path = case map lowercase (splitDirectories path) of
1613 "dist" :_ -> True
1614 ".":"dist":_ -> True
1615 _ -> False
1617 -- paths that must be relative
1618 relPaths :: [(FilePath, String, PathKind)]
1619 relPaths =
1620 [ (path, "extra-source-files", PathKindGlob) | path <- extraSrcFiles pkg ] ++
1621 [ (path, "extra-tmp-files", PathKindFile) | path <- extraTmpFiles pkg ] ++
1622 [ (path, "extra-doc-files", PathKindGlob) | path <- extraDocFiles pkg ] ++
1623 [ (path, "data-files", PathKindGlob) | path <- dataFiles pkg ] ++
1624 [ (path, "data-dir", PathKindDirectory) | path <- [dataDir pkg]] ++
1625 [ (path, "license-file", PathKindFile) | path <- map getSymbolicPath $ licenseFiles pkg ] ++
1626 concat
1627 [ [ (path, "asm-sources", PathKindFile) | path <- asmSources bi ] ++
1628 [ (path, "cmm-sources", PathKindFile) | path <- cmmSources bi ] ++
1629 [ (path, "c-sources", PathKindFile) | path <- cSources bi ] ++
1630 [ (path, "cxx-sources", PathKindFile) | path <- cxxSources bi ] ++
1631 [ (path, "js-sources", PathKindFile) | path <- jsSources bi ] ++
1632 [ (path, "install-includes", PathKindFile) | path <- installIncludes bi ] ++
1633 [ (path, "hs-source-dirs", PathKindDirectory) | path <- map getSymbolicPath $ hsSourceDirs bi ]
1634 | bi <- allBuildInfo pkg
1637 -- paths that are allowed to be absolute
1638 absPaths :: [(FilePath, String, PathKind)]
1639 absPaths = concat
1640 [ [ (path, "includes", PathKindFile) | path <- includes bi ] ++
1641 [ (path, "include-dirs", PathKindDirectory) | path <- includeDirs bi ] ++
1642 [ (path, "extra-lib-dirs", PathKindDirectory) | path <- extraLibDirs bi ] ++
1643 [ (path, "extra-lib-dirs-static", PathKindDirectory) | path <- extraLibDirsStatic bi ]
1644 | bi <- allBuildInfo pkg
1647 --TODO: check sets of paths that would be interpreted differently between Unix
1648 -- and windows, ie case-sensitive or insensitive. Things that might clash, or
1649 -- conversely be distinguished.
1651 --TODO: use the tar path checks on all the above paths
1653 -- | Check that the package declares the version in the @\"cabal-version\"@
1654 -- field correctly.
1656 checkCabalVersion :: PackageDescription -> [PackageCheck]
1657 checkCabalVersion pkg =
1658 catMaybes [
1660 -- check use of test suite sections
1661 checkVersion CabalSpecV1_8 (not (null $ testSuites pkg)) $
1662 PackageDistInexcusable CVTestSuite
1664 -- check use of default-language field
1665 -- note that we do not need to do an equivalent check for the
1666 -- other-language field since that one does not change behaviour
1667 , checkVersion CabalSpecV1_10 (any isJust (buildInfoField defaultLanguage)) $
1668 PackageBuildWarning CVDefaultLanguage
1670 , check (specVersion pkg >= CabalSpecV1_10 && specVersion pkg < CabalSpecV3_4
1671 && any isNothing (buildInfoField defaultLanguage)) $
1672 PackageBuildWarning CVDefaultLanguageComponent
1674 , checkVersion CabalSpecV1_18
1675 (not . null $ extraDocFiles pkg) $
1676 PackageDistInexcusable CVExtraDocFiles
1678 , checkVersion CabalSpecV2_0
1679 (not (null (subLibraries pkg))) $
1680 PackageDistInexcusable CVMultiLib
1682 -- check use of reexported-modules sections
1683 , checkVersion CabalSpecV1_22
1684 (any (not.null.reexportedModules) (allLibraries pkg)) $
1685 PackageDistInexcusable CVReexported
1687 -- check use of thinning and renaming
1688 , checkVersion CabalSpecV2_0 usesBackpackIncludes $
1689 PackageDistInexcusable CVMixins
1691 -- check use of 'extra-framework-dirs' field
1692 , checkVersion CabalSpecV1_24 (any (not . null) (buildInfoField extraFrameworkDirs)) $
1693 -- Just a warning, because this won't break on old Cabal versions.
1694 PackageDistSuspiciousWarn CVExtraFrameworkDirs
1696 -- check use of default-extensions field
1697 -- don't need to do the equivalent check for other-extensions
1698 , checkVersion CabalSpecV1_10 (any (not . null) (buildInfoField defaultExtensions)) $
1699 PackageBuildWarning CVDefaultExtensions
1701 -- check use of extensions field
1702 , check (specVersion pkg >= CabalSpecV1_10
1703 && any (not . null) (buildInfoField oldExtensions)) $
1704 PackageBuildWarning CVExtensionsDeprecated
1706 , checkVersion CabalSpecV3_0 (any (not . null)
1707 (concatMap buildInfoField
1708 [ asmSources
1709 , cmmSources
1710 , extraBundledLibs
1711 , extraLibFlavours ])) $
1712 PackageDistInexcusable CVSources
1714 , checkVersion CabalSpecV3_0 (any (not . null) $ buildInfoField extraDynLibFlavours) $
1715 PackageDistInexcusable
1716 (CVExtraDynamic $ buildInfoField extraDynLibFlavours)
1718 , checkVersion CabalSpecV2_2 (any (not . null)
1719 (buildInfoField virtualModules)) $
1720 PackageDistInexcusable CVVirtualModules
1722 -- check use of "source-repository" section
1723 , checkVersion CabalSpecV1_6 (not (null (sourceRepos pkg))) $
1724 PackageDistInexcusable CVSourceRepository
1726 -- check for new language extensions
1727 , checkVersion CabalSpecV1_2 (not (null mentionedExtensionsThatNeedCabal12)) $
1728 PackageDistInexcusable
1729 (CVExtensions CabalSpecV1_2 mentionedExtensionsThatNeedCabal12)
1731 , checkVersion CabalSpecV1_4 (not (null mentionedExtensionsThatNeedCabal14)) $
1732 PackageDistInexcusable
1733 (CVExtensions CabalSpecV1_4 mentionedExtensionsThatNeedCabal14)
1735 , check (specVersion pkg >= CabalSpecV1_24
1736 && isNothing (setupBuildInfo pkg)
1737 && buildType pkg == Custom) $
1738 PackageBuildWarning CVCustomSetup
1740 , check (specVersion pkg < CabalSpecV1_24
1741 && isNothing (setupBuildInfo pkg)
1742 && buildType pkg == Custom) $
1743 PackageDistSuspiciousWarn CVExpliticDepsCustomSetup
1745 , check (specVersion pkg >= CabalSpecV2_0
1746 && elem (autogenPathsModuleName pkg) allModuleNames
1747 && not (elem (autogenPathsModuleName pkg) allModuleNamesAutogen) ) $
1748 PackageDistInexcusable CVAutogenPaths
1751 where
1752 -- Perform a check on packages that use a version of the spec less than
1753 -- the version given. This is for cases where a new Cabal version adds
1754 -- a new feature and we want to check that it is not used prior to that
1755 -- version.
1756 checkVersion :: CabalSpecVersion -> Bool -> PackageCheck -> Maybe PackageCheck
1757 checkVersion ver cond pc
1758 | specVersion pkg >= ver = Nothing
1759 | otherwise = check cond pc
1761 buildInfoField field = map field (allBuildInfo pkg)
1763 usesBackpackIncludes = any (not . null . mixins) (allBuildInfo pkg)
1765 mentionedExtensions = [ ext | bi <- allBuildInfo pkg
1766 , ext <- allExtensions bi ]
1767 mentionedExtensionsThatNeedCabal12 =
1768 nub (filter (`elem` compatExtensionsExtra) mentionedExtensions)
1770 -- As of Cabal-1.4 we can add new extensions without worrying about
1771 -- breaking old versions of cabal.
1772 mentionedExtensionsThatNeedCabal14 =
1773 nub (filter (`notElem` compatExtensions) mentionedExtensions)
1775 -- The known extensions in Cabal-1.2.3
1776 compatExtensions =
1777 map EnableExtension
1778 [ OverlappingInstances, UndecidableInstances, IncoherentInstances
1779 , RecursiveDo, ParallelListComp, MultiParamTypeClasses
1780 , FunctionalDependencies, Rank2Types
1781 , RankNTypes, PolymorphicComponents, ExistentialQuantification
1782 , ScopedTypeVariables, ImplicitParams, FlexibleContexts
1783 , FlexibleInstances, EmptyDataDecls, CPP, BangPatterns
1784 , TypeSynonymInstances, TemplateHaskell, ForeignFunctionInterface
1785 , Arrows, Generics, NamedFieldPuns, PatternGuards
1786 , GeneralizedNewtypeDeriving, ExtensibleRecords, RestrictedTypeSynonyms
1787 , HereDocuments] ++
1788 map DisableExtension
1789 [MonomorphismRestriction, ImplicitPrelude] ++
1790 compatExtensionsExtra
1792 -- The extra known extensions in Cabal-1.2.3 vs Cabal-1.1.6
1793 -- (Cabal-1.1.6 came with ghc-6.6. Cabal-1.2 came with ghc-6.8)
1794 compatExtensionsExtra =
1795 map EnableExtension
1796 [ KindSignatures, MagicHash, TypeFamilies, StandaloneDeriving
1797 , UnicodeSyntax, PatternSignatures, UnliftedFFITypes, LiberalTypeSynonyms
1798 , TypeOperators, RecordWildCards, RecordPuns, DisambiguateRecordFields
1799 , OverloadedStrings, GADTs, RelaxedPolyRec
1800 , ExtendedDefaultRules, UnboxedTuples, DeriveDataTypeable
1801 , ConstrainedClassMethods
1802 ] ++
1803 map DisableExtension
1804 [MonoPatBinds]
1806 allModuleNames =
1807 (case library pkg of
1808 Nothing -> []
1809 (Just lib) -> explicitLibModules lib
1811 ++ concatMap otherModules (allBuildInfo pkg)
1813 allModuleNamesAutogen = concatMap autogenModules (allBuildInfo pkg)
1815 -- ------------------------------------------------------------
1816 -- * Checks on the GenericPackageDescription
1817 -- ------------------------------------------------------------
1819 -- | Check the build-depends fields for any weirdness or bad practice.
1821 checkPackageVersions :: GenericPackageDescription -> [PackageCheck]
1822 checkPackageVersions pkg =
1823 catMaybes [
1825 -- Check that the version of base is bounded above.
1826 -- For example this bans "build-depends: base >= 3".
1827 -- It should probably be "build-depends: base >= 3 && < 4"
1828 -- which is the same as "build-depends: base == 3.*"
1829 check (not (hasUpperBound baseDependency)) $
1830 PackageDistInexcusable BaseNoUpperBounds
1833 where
1834 baseDependency = case typicalPkg pkg of
1835 Right (pkg', _) | not (null baseDeps) ->
1836 foldr intersectVersionRanges anyVersion baseDeps
1837 where
1838 baseDeps =
1839 [ vr | Dependency pname vr _ <- allBuildDepends pkg'
1840 , pname == mkPackageName "base" ]
1842 -- Just in case finalizePD fails for any reason,
1843 -- or if the package doesn't depend on the base package at all,
1844 -- then we will just skip the check, since hasUpperBound noVersion = True
1845 _ -> noVersion
1847 checkConditionals :: GenericPackageDescription -> [PackageCheck]
1848 checkConditionals pkg =
1849 catMaybes [
1851 check (not $ null unknownOSs) $
1852 PackageDistInexcusable (UnknownOS unknownOSs)
1854 , check (not $ null unknownArches) $
1855 PackageDistInexcusable (UnknownArch unknownArches)
1857 , check (not $ null unknownImpls) $
1858 PackageDistInexcusable (UnknownCompiler unknownImpls)
1860 where
1861 unknownOSs = [ os | OS (OtherOS os) <- conditions ]
1862 unknownArches = [ arch | Arch (OtherArch arch) <- conditions ]
1863 unknownImpls = [ impl | Impl (OtherCompiler impl) _ <- conditions ]
1864 conditions = concatMap fvs (maybeToList (condLibrary pkg))
1865 ++ concatMap (fvs . snd) (condSubLibraries pkg)
1866 ++ concatMap (fvs . snd) (condForeignLibs pkg)
1867 ++ concatMap (fvs . snd) (condExecutables pkg)
1868 ++ concatMap (fvs . snd) (condTestSuites pkg)
1869 ++ concatMap (fvs . snd) (condBenchmarks pkg)
1870 fvs (CondNode _ _ ifs) = concatMap compfv ifs -- free variables
1871 compfv (CondBranch c ct mct) = condfv c ++ fvs ct ++ maybe [] fvs mct
1872 condfv c = case c of
1873 Var v -> [v]
1874 Lit _ -> []
1875 CNot c1 -> condfv c1
1876 COr c1 c2 -> condfv c1 ++ condfv c2
1877 CAnd c1 c2 -> condfv c1 ++ condfv c2
1879 checkFlagNames :: GenericPackageDescription -> [PackageCheck]
1880 checkFlagNames gpd
1881 | null invalidFlagNames = []
1882 | otherwise =
1883 [ PackageDistInexcusable (SuspiciousFlagName invalidFlagNames) ]
1884 where
1885 invalidFlagNames =
1886 [ fn
1887 | flag <- genPackageFlags gpd
1888 , let fn = unFlagName (flagName flag)
1889 , invalidFlagName fn
1891 -- starts with dash
1892 invalidFlagName ('-':_) = True
1893 -- mon ascii letter
1894 invalidFlagName cs = any (not . isAscii) cs
1896 checkUnusedFlags :: GenericPackageDescription -> [PackageCheck]
1897 checkUnusedFlags gpd
1898 | declared == used = []
1899 | otherwise =
1900 [ PackageDistSuspicious (DeclaredUsedFlags declared used) ]
1901 where
1902 declared :: Set.Set FlagName
1903 declared = toSetOf (L.genPackageFlags . traverse . L.flagName) gpd
1905 used :: Set.Set FlagName
1906 used = mconcat
1907 [ toSetOf (L.condLibrary . traverse . traverseCondTreeV . L._PackageFlag) gpd
1908 , toSetOf (L.condSubLibraries . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd
1909 , toSetOf (L.condForeignLibs . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd
1910 , toSetOf (L.condExecutables . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd
1911 , toSetOf (L.condTestSuites . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd
1912 , toSetOf (L.condBenchmarks . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd
1915 checkUnicodeXFields :: GenericPackageDescription -> [PackageCheck]
1916 checkUnicodeXFields gpd
1917 | null nonAsciiXFields = []
1918 | otherwise =
1919 [ PackageDistInexcusable (NonASCIICustomField nonAsciiXFields) ]
1920 where
1921 nonAsciiXFields :: [String]
1922 nonAsciiXFields = [ n | (n, _) <- xfields, any (not . isAscii) n ]
1924 xfields :: [(String,String)]
1925 xfields = DList.runDList $ mconcat
1926 [ toDListOf (L.packageDescription . L.customFieldsPD . traverse) gpd
1927 , toDListOf (L.traverseBuildInfos . L.customFieldsBI . traverse) gpd
1930 -- | cabal-version <2.2 + Paths_module + default-extensions: doesn't build.
1931 checkPathsModuleExtensions :: PackageDescription -> [PackageCheck]
1932 checkPathsModuleExtensions pd
1933 | specVersion pd >= CabalSpecV2_2 = []
1934 | any checkBI (allBuildInfo pd) || any checkLib (allLibraries pd)
1935 = return (PackageBuildImpossible RebindableClash)
1936 | otherwise = []
1937 where
1938 mn = autogenPathsModuleName pd
1940 checkLib :: Library -> Bool
1941 checkLib l = mn `elem` exposedModules l && checkExts (l ^. L.defaultExtensions)
1943 checkBI :: BuildInfo -> Bool
1944 checkBI bi =
1945 (mn `elem` otherModules bi || mn `elem` autogenModules bi) &&
1946 checkExts (bi ^. L.defaultExtensions)
1948 checkExts exts = rebind `elem` exts && (strings `elem` exts || lists `elem` exts)
1949 where
1950 rebind = EnableExtension RebindableSyntax
1951 strings = EnableExtension OverloadedStrings
1952 lists = EnableExtension OverloadedLists
1954 -- | Checks GHC options from all ghc-*-options fields from the given BuildInfo
1955 -- and reports flags that are OK during development process, but are
1956 -- unacceptable in a distributed package
1957 checkDevelopmentOnlyFlagsBuildInfo :: BuildInfo -> [PackageCheck]
1958 checkDevelopmentOnlyFlagsBuildInfo bi =
1959 checkDevelopmentOnlyFlagsOptions "ghc-options" (hcOptions GHC bi)
1960 ++ checkDevelopmentOnlyFlagsOptions "ghc-prof-options" (hcProfOptions GHC bi)
1961 ++ checkDevelopmentOnlyFlagsOptions "ghc-shared-options" (hcSharedOptions GHC bi)
1963 -- | Checks the given list of flags belonging to the given field and reports
1964 -- flags that are OK during development process, but are unacceptable in a
1965 -- distributed package
1966 checkDevelopmentOnlyFlagsOptions :: String -> [String] -> [PackageCheck]
1967 checkDevelopmentOnlyFlagsOptions fieldName ghcOptions =
1968 catMaybes [
1970 check has_Werror $
1971 PackageDistInexcusable (WErrorUnneeded fieldName)
1973 , check has_J $
1974 PackageDistInexcusable (JUnneeded fieldName)
1976 , checkFlags ["-fdefer-type-errors"] $
1977 PackageDistInexcusable (FDeferTypeErrorsUnneeded fieldName)
1979 -- -dynamic is not a debug flag
1980 , check (any (\opt -> "-d" `isPrefixOf` opt && opt /= "-dynamic")
1981 ghcOptions) $
1982 PackageDistInexcusable (DynamicUnneeded fieldName)
1984 , checkFlags ["-fprof-auto", "-fprof-auto-top", "-fprof-auto-calls",
1985 "-fprof-cafs", "-fno-prof-count-entries",
1986 "-auto-all", "-auto", "-caf-all"] $
1987 PackageDistSuspicious (ProfilingUnneeded fieldName)
1989 where
1991 has_Werror = "-Werror" `elem` ghcOptions
1992 has_J = any
1993 (\o -> case o of
1994 "-j" -> True
1995 ('-' : 'j' : d : _) -> isDigit d
1996 _ -> False
1998 ghcOptions
1999 checkFlags :: [String] -> PackageCheck -> Maybe PackageCheck
2000 checkFlags flags = check (any (`elem` flags) ghcOptions)
2002 checkDevelopmentOnlyFlags :: GenericPackageDescription -> [PackageCheck]
2003 checkDevelopmentOnlyFlags pkg =
2004 concatMap checkDevelopmentOnlyFlagsBuildInfo
2005 [ bi
2006 | (conditions, bi) <- allConditionalBuildInfo
2007 , not (any guardedByManualFlag conditions) ]
2008 where
2009 guardedByManualFlag = definitelyFalse
2011 -- We've basically got three-values logic here: True, False or unknown
2012 -- hence this pattern to propagate the unknown cases properly.
2013 definitelyFalse (Var (PackageFlag n)) = maybe False not (Map.lookup n manualFlags)
2014 definitelyFalse (Var _) = False
2015 definitelyFalse (Lit b) = not b
2016 definitelyFalse (CNot c) = definitelyTrue c
2017 definitelyFalse (COr c1 c2) = definitelyFalse c1 && definitelyFalse c2
2018 definitelyFalse (CAnd c1 c2) = definitelyFalse c1 || definitelyFalse c2
2020 definitelyTrue (Var (PackageFlag n)) = fromMaybe False (Map.lookup n manualFlags)
2021 definitelyTrue (Var _) = False
2022 definitelyTrue (Lit b) = b
2023 definitelyTrue (CNot c) = definitelyFalse c
2024 definitelyTrue (COr c1 c2) = definitelyTrue c1 || definitelyTrue c2
2025 definitelyTrue (CAnd c1 c2) = definitelyTrue c1 && definitelyTrue c2
2027 manualFlags = Map.fromList
2028 [ (flagName flag, flagDefault flag)
2029 | flag <- genPackageFlags pkg
2030 , flagManual flag ]
2032 allConditionalBuildInfo :: [([Condition ConfVar], BuildInfo)]
2033 allConditionalBuildInfo =
2034 concatMap (collectCondTreePaths libBuildInfo)
2035 (maybeToList (condLibrary pkg))
2037 ++ concatMap (collectCondTreePaths libBuildInfo . snd)
2038 (condSubLibraries pkg)
2040 ++ concatMap (collectCondTreePaths buildInfo . snd)
2041 (condExecutables pkg)
2043 ++ concatMap (collectCondTreePaths testBuildInfo . snd)
2044 (condTestSuites pkg)
2046 ++ concatMap (collectCondTreePaths benchmarkBuildInfo . snd)
2047 (condBenchmarks pkg)
2049 -- get all the leaf BuildInfo, paired up with the path (in the tree sense)
2050 -- of if-conditions that guard it
2051 collectCondTreePaths :: (a -> b)
2052 -> CondTree v c a
2053 -> [([Condition v], b)]
2054 collectCondTreePaths mapData = go []
2055 where
2056 go conditions condNode =
2057 -- the data at this level in the tree:
2058 (reverse conditions, mapData (condTreeData condNode))
2060 : concat
2061 [ go (condition:conditions) ifThen
2062 | (CondBranch condition ifThen _) <- condTreeComponents condNode ]
2064 ++ concat
2065 [ go (condition:conditions) elseThen
2066 | (CondBranch condition _ (Just elseThen)) <- condTreeComponents condNode ]
2069 -- ------------------------------------------------------------
2070 -- * Checks involving files in the package
2071 -- ------------------------------------------------------------
2073 -- | Sanity check things that requires IO. It looks at the files in the
2074 -- package and expects to find the package unpacked in at the given file path.
2076 checkPackageFiles :: Verbosity -> PackageDescription -> FilePath -> IO [PackageCheck]
2077 checkPackageFiles verbosity pkg root = do
2078 contentChecks <- checkPackageContent checkFilesIO pkg
2079 preDistributionChecks <- checkPackageFilesPreDistribution verbosity pkg root
2080 -- Sort because different platforms will provide files from
2081 -- `getDirectoryContents` in different orders, and we'd like to be
2082 -- stable for test output.
2083 return (sort contentChecks ++ sort preDistributionChecks)
2084 where
2085 checkFilesIO = CheckPackageContentOps {
2086 doesFileExist = System.doesFileExist . relative,
2087 doesDirectoryExist = System.doesDirectoryExist . relative,
2088 getDirectoryContents = System.Directory.getDirectoryContents . relative,
2089 getFileContents = BS.readFile . relative
2091 relative path = root </> path
2093 -- | A record of operations needed to check the contents of packages.
2094 -- Used by 'checkPackageContent'.
2096 data CheckPackageContentOps m = CheckPackageContentOps {
2097 doesFileExist :: FilePath -> m Bool,
2098 doesDirectoryExist :: FilePath -> m Bool,
2099 getDirectoryContents :: FilePath -> m [FilePath],
2100 getFileContents :: FilePath -> m BS.ByteString
2103 -- | Sanity check things that requires looking at files in the package.
2104 -- This is a generalised version of 'checkPackageFiles' that can work in any
2105 -- monad for which you can provide 'CheckPackageContentOps' operations.
2107 -- The point of this extra generality is to allow doing checks in some virtual
2108 -- file system, for example a tarball in memory.
2110 checkPackageContent :: (Monad m, Applicative m)
2111 => CheckPackageContentOps m
2112 -> PackageDescription
2113 -> m [PackageCheck]
2114 checkPackageContent ops pkg = do
2115 cabalBomError <- checkCabalFileBOM ops
2116 cabalNameError <- checkCabalFileName ops pkg
2117 licenseErrors <- checkLicensesExist ops pkg
2118 setupError <- checkSetupExists ops pkg
2119 configureError <- checkConfigureExists ops pkg
2120 localPathErrors <- checkLocalPathsExist ops pkg
2121 vcsLocation <- checkMissingVcsInfo ops pkg
2123 return $ licenseErrors
2124 ++ catMaybes [cabalBomError, cabalNameError, setupError, configureError]
2125 ++ localPathErrors
2126 ++ vcsLocation
2128 checkCabalFileBOM :: Monad m => CheckPackageContentOps m
2129 -> m (Maybe PackageCheck)
2130 checkCabalFileBOM ops = do
2131 epdfile <- findPackageDesc ops
2132 case epdfile of
2133 -- MASSIVE HACK. If the Cabal file doesn't exist, that is
2134 -- a very strange situation to be in, because the driver code
2135 -- in 'Distribution.Setup' ought to have noticed already!
2136 -- But this can be an issue, see #3552 and also when
2137 -- --cabal-file is specified. So if you can't find the file,
2138 -- just don't bother with this check.
2139 Left _ -> return Nothing
2140 Right pdfile -> (flip check pc . BS.isPrefixOf bomUtf8)
2141 `liftM` getFileContents ops pdfile
2142 where pc = PackageDistInexcusable (BOMStart pdfile)
2144 where
2145 bomUtf8 :: BS.ByteString
2146 bomUtf8 = BS.pack [0xef,0xbb,0xbf] -- U+FEFF encoded as UTF8
2148 checkCabalFileName :: Monad m => CheckPackageContentOps m
2149 -> PackageDescription
2150 -> m (Maybe PackageCheck)
2151 checkCabalFileName ops pkg = do
2152 -- findPackageDesc already takes care to detect missing/multiple
2153 -- .cabal files; we don't include this check in 'findPackageDesc' in
2154 -- order not to short-cut other checks which call 'findPackageDesc'
2155 epdfile <- findPackageDesc ops
2156 case epdfile of
2157 -- see "MASSIVE HACK" note in 'checkCabalFileBOM'
2158 Left _ -> return Nothing
2159 Right pdfile
2160 | takeFileName pdfile == expectedCabalname -> return Nothing
2161 | otherwise -> return $ Just $ PackageDistInexcusable
2162 (NotPackageName pdfile expectedCabalname)
2163 where
2164 pkgname = unPackageName . packageName $ pkg
2165 expectedCabalname = pkgname <.> "cabal"
2168 -- |Find a package description file in the given directory. Looks for
2169 -- @.cabal@ files. Like 'Distribution.Simple.Utils.findPackageDesc',
2170 -- but generalized over monads.
2171 findPackageDesc :: Monad m => CheckPackageContentOps m
2172 -> m (Either PackageCheck FilePath) -- ^<pkgname>.cabal
2173 findPackageDesc ops
2174 = do let dir = "."
2175 files <- getDirectoryContents ops dir
2176 -- to make sure we do not mistake a ~/.cabal/ dir for a <pkgname>.cabal
2177 -- file we filter to exclude dirs and null base file names:
2178 cabalFiles <- filterM (doesFileExist ops)
2179 [ dir </> file
2180 | file <- files
2181 , let (name, ext) = splitExtension file
2182 , not (null name) && ext == ".cabal" ]
2183 case cabalFiles of
2184 [] -> return (Left $ PackageBuildImpossible NoDesc)
2185 [cabalFile] -> return (Right cabalFile)
2186 multiple -> return (Left $ PackageBuildImpossible
2187 (MultiDesc multiple))
2189 checkLicensesExist :: (Monad m, Applicative m)
2190 => CheckPackageContentOps m
2191 -> PackageDescription
2192 -> m [PackageCheck]
2193 checkLicensesExist ops pkg = do
2194 exists <- traverse (doesFileExist ops . getSymbolicPath) (licenseFiles pkg)
2195 return
2196 [ PackageBuildWarning (UnknownFile fieldname file)
2197 | (file, False) <- zip (licenseFiles pkg) exists ]
2198 where
2199 fieldname | length (licenseFiles pkg) == 1 = "license-file"
2200 | otherwise = "license-files"
2202 checkSetupExists :: Monad m => CheckPackageContentOps m
2203 -> PackageDescription
2204 -> m (Maybe PackageCheck)
2205 checkSetupExists ops pkg = do
2206 let simpleBuild = buildType pkg == Simple
2207 hsexists <- doesFileExist ops "Setup.hs"
2208 lhsexists <- doesFileExist ops "Setup.lhs"
2209 return $ check (not simpleBuild && not hsexists && not lhsexists) $
2210 PackageDistInexcusable MissingSetupFile
2212 checkConfigureExists :: Monad m => CheckPackageContentOps m
2213 -> PackageDescription
2214 -> m (Maybe PackageCheck)
2215 checkConfigureExists ops pd
2216 | buildType pd == Configure = do
2217 exists <- doesFileExist ops "configure"
2218 return $ check (not exists) $
2219 PackageBuildWarning MissingConfigureScript
2220 | otherwise = return Nothing
2222 checkLocalPathsExist :: Monad m => CheckPackageContentOps m
2223 -> PackageDescription
2224 -> m [PackageCheck]
2225 checkLocalPathsExist ops pkg = do
2226 let dirs = [ (dir, kind)
2227 | bi <- allBuildInfo pkg
2228 , (dir, kind) <-
2229 [ (dir, "extra-lib-dirs") | dir <- extraLibDirs bi ]
2230 ++ [ (dir, "extra-lib-dirs-static") | dir <- extraLibDirsStatic bi ]
2231 ++ [ (dir, "extra-framework-dirs")
2232 | dir <- extraFrameworkDirs bi ]
2233 ++ [ (dir, "include-dirs") | dir <- includeDirs bi ]
2234 ++ [ (getSymbolicPath dir, "hs-source-dirs") | dir <- hsSourceDirs bi ]
2235 , isRelativeOnAnyPlatform dir ]
2236 missing <- filterM (liftM not . doesDirectoryExist ops . fst) dirs
2237 return [ PackageBuildWarning (UnknownDirectory kind dir)
2238 | (dir, kind) <- missing ]
2240 checkMissingVcsInfo :: (Monad m, Applicative m)
2241 => CheckPackageContentOps m
2242 -> PackageDescription
2243 -> m [PackageCheck]
2244 checkMissingVcsInfo ops pkg | null (sourceRepos pkg) = do
2245 vcsInUse <- liftM or $ traverse (doesDirectoryExist ops) repoDirnames
2246 if vcsInUse
2247 then return [ PackageDistSuspicious MissingSourceControl ]
2248 else return []
2249 where
2250 repoDirnames = [ dirname | repo <- knownRepoTypes
2251 , dirname <- repoTypeDirname repo]
2253 checkMissingVcsInfo _ _ = return []
2255 repoTypeDirname :: KnownRepoType -> [FilePath]
2256 repoTypeDirname Darcs = ["_darcs"]
2257 repoTypeDirname Git = [".git"]
2258 repoTypeDirname SVN = [".svn"]
2259 repoTypeDirname CVS = ["CVS"]
2260 repoTypeDirname Mercurial = [".hg"]
2261 repoTypeDirname GnuArch = [".arch-params"]
2262 repoTypeDirname Bazaar = [".bzr"]
2263 repoTypeDirname Monotone = ["_MTN"]
2264 repoTypeDirname Pijul = [".pijul"]
2266 -- ------------------------------------------------------------
2267 -- * Checks involving files in the package
2268 -- ------------------------------------------------------------
2270 -- | Check the names of all files in a package for portability problems. This
2271 -- should be done for example when creating or validating a package tarball.
2273 checkPackageFileNames :: [FilePath] -> [PackageCheck]
2274 checkPackageFileNames = checkPackageFileNamesWithGlob . zip (repeat True)
2276 checkPackageFileNamesWithGlob :: [(Bool, FilePath)] -> [PackageCheck]
2277 checkPackageFileNamesWithGlob files =
2278 catMaybes $
2279 checkWindowsPaths files
2281 [ checkTarPath file
2282 | (_, file) <- files
2285 checkWindowsPaths :: [(Bool, FilePath)] -> Maybe PackageCheck
2286 checkWindowsPaths paths =
2287 case filter (not . FilePath.Windows.isValid . escape) paths of
2288 [] -> Nothing
2289 ps -> Just $
2290 PackageDistInexcusable (InvalidOnWin $ map snd ps)
2291 where
2292 -- force a relative name to catch invalid file names like "f:oo" which
2293 -- otherwise parse as file "oo" in the current directory on the 'f' drive.
2294 escape (isGlob, path) = (".\\" ++)
2295 -- glob paths will be expanded before being dereferenced, so asterisks
2296 -- shouldn't count against them.
2297 $ map (\c -> if c == '*' && isGlob then 'x' else c) path
2299 -- | Check a file name is valid for the portable POSIX tar format.
2301 -- The POSIX tar format has a restriction on the length of file names. It is
2302 -- unfortunately not a simple restriction like a maximum length. The exact
2303 -- restriction is that either the whole path be 100 characters or less, or it
2304 -- be possible to split the path on a directory separator such that the first
2305 -- part is 155 characters or less and the second part 100 characters or less.
2307 checkTarPath :: FilePath -> Maybe PackageCheck
2308 checkTarPath path
2309 | length path > 255 = Just longPath
2310 | otherwise = case pack nameMax (reverse (splitPath path)) of
2311 Left err -> Just err
2312 Right [] -> Nothing
2313 Right (h:rest) -> case pack prefixMax remainder of
2314 Left err -> Just err
2315 Right [] -> Nothing
2316 Right (_:_) -> Just noSplit
2317 where
2318 -- drop the '/' between the name and prefix:
2319 remainder = safeInit h : rest
2321 where
2322 nameMax, prefixMax :: Int
2323 nameMax = 100
2324 prefixMax = 155
2326 pack _ [] = Left emptyName
2327 pack maxLen (c:cs)
2328 | n > maxLen = Left longName
2329 | otherwise = Right (pack' maxLen n cs)
2330 where n = length c
2332 pack' maxLen n (c:cs)
2333 | n' <= maxLen = pack' maxLen n' cs
2334 where n' = n + length c
2335 pack' _ _ cs = cs
2337 longPath = PackageDistInexcusable (FilePathTooLong path)
2338 longName = PackageDistInexcusable (FilePathNameTooLong path)
2339 noSplit = PackageDistInexcusable (FilePathSplitTooLong path)
2340 emptyName = PackageDistInexcusable FilePathEmpty
2342 -- --------------------------------------------------------------
2343 -- * Checks for missing content and other pre-distribution checks
2344 -- --------------------------------------------------------------
2346 -- | Similar to 'checkPackageContent', 'checkPackageFilesPreDistribution'
2347 -- inspects the files included in the package, but is primarily looking for
2348 -- files in the working tree that may have been missed or other similar
2349 -- problems that can only be detected pre-distribution.
2351 -- Because Hackage necessarily checks the uploaded tarball, it is too late to
2352 -- check these on the server; these checks only make sense in the development
2353 -- and package-creation environment. Hence we can use IO, rather than needing
2354 -- to pass a 'CheckPackageContentOps' dictionary around.
2355 checkPackageFilesPreDistribution :: Verbosity -> PackageDescription -> FilePath -> IO [PackageCheck]
2356 -- Note: this really shouldn't return any 'Inexcusable' warnings,
2357 -- because that will make us say that Hackage would reject the package.
2358 -- But, because Hackage doesn't run these tests, that will be a lie!
2359 checkPackageFilesPreDistribution = checkGlobFiles
2361 -- | Discover problems with the package's wildcards.
2362 checkGlobFiles :: Verbosity
2363 -> PackageDescription
2364 -> FilePath
2365 -> IO [PackageCheck]
2366 checkGlobFiles verbosity pkg root =
2367 fmap concat $ for allGlobs $ \(field, dir, glob) ->
2368 -- Note: we just skip over parse errors here; they're reported elsewhere.
2369 case parseFileGlob (specVersion pkg) glob of
2370 Left _ -> return []
2371 Right parsedGlob -> do
2372 results <- runDirFileGlob verbosity (root </> dir) parsedGlob
2373 let individualWarnings = results >>= getWarning field glob
2374 noMatchesWarning =
2375 [ PackageDistSuspiciousWarn (GlobNoMatch field glob)
2376 | all (not . suppressesNoMatchesWarning) results
2378 return (noMatchesWarning ++ individualWarnings)
2379 where
2380 adjustedDataDir = if null (dataDir pkg) then "." else dataDir pkg
2381 allGlobs = concat
2382 [ (,,) "extra-source-files" "." <$> extraSrcFiles pkg
2383 , (,,) "extra-doc-files" "." <$> extraDocFiles pkg
2384 , (,,) "data-files" adjustedDataDir <$> dataFiles pkg
2387 -- If there's a missing directory in play, since our globs don't
2388 -- (currently) support disjunction, that will always mean there are no
2389 -- matches. The no matches error in this case is strictly less informative
2390 -- than the missing directory error, so sit on it.
2391 suppressesNoMatchesWarning (GlobMatch _) = True
2392 suppressesNoMatchesWarning (GlobWarnMultiDot _) = False
2393 suppressesNoMatchesWarning (GlobMissingDirectory _) = True
2395 getWarning :: String -> FilePath -> GlobResult FilePath -> [PackageCheck]
2396 getWarning _ _ (GlobMatch _) =
2398 -- Before Cabal 2.4, the extensions of globs had to match the file
2399 -- exactly. This has been relaxed in 2.4 to allow matching only the
2400 -- suffix. This warning detects when pre-2.4 package descriptions are
2401 -- omitting files purely because of the stricter check.
2402 getWarning field glob (GlobWarnMultiDot file) =
2403 [ PackageDistSuspiciousWarn (GlobExactMatch field glob file) ]
2404 getWarning field glob (GlobMissingDirectory dir) =
2405 [ PackageDistSuspiciousWarn (GlobNoDir field glob dir) ]
2407 -- | Check that setup dependencies, have proper bounds.
2408 -- In particular, @base@ and @Cabal@ upper bounds are mandatory.
2409 checkSetupVersions :: GenericPackageDescription -> [PackageCheck]
2410 checkSetupVersions pkg =
2411 [ emitError nameStr
2412 | (name, vr) <- Map.toList deps
2413 , not (hasUpperBound vr)
2414 , let nameStr = unPackageName name
2415 , nameStr `elem` criticalPkgs
2417 where
2418 criticalPkgs = ["Cabal", "base"]
2419 deps = case typicalPkg pkg of
2420 Right (pkgs', _) ->
2421 Map.fromListWith intersectVersionRanges
2422 [ (pname, vr)
2423 | sbi <- maybeToList $ setupBuildInfo pkgs'
2424 , Dependency pname vr _ <- setupDepends sbi
2426 _ -> Map.empty
2427 emitError nm =
2428 PackageDistInexcusable (UpperBoundSetup nm)
2430 checkDuplicateModules :: GenericPackageDescription -> [PackageCheck]
2431 checkDuplicateModules pkg =
2432 concatMap checkLib (maybe id (:) (condLibrary pkg) . map snd $ condSubLibraries pkg)
2433 ++ concatMap checkExe (map snd $ condExecutables pkg)
2434 ++ concatMap checkTest (map snd $ condTestSuites pkg)
2435 ++ concatMap checkBench (map snd $ condBenchmarks pkg)
2436 where
2437 -- the duplicate modules check is has not been thoroughly vetted for backpack
2438 checkLib = checkDups "library" (\l -> explicitLibModules l ++ map moduleReexportName (reexportedModules l))
2439 checkExe = checkDups "executable" exeModules
2440 checkTest = checkDups "test suite" testModules
2441 checkBench = checkDups "benchmark" benchmarkModules
2442 checkDups s getModules t =
2443 let sumPair (x,x') (y,y') = (x + x' :: Int, y + y' :: Int)
2444 mergePair (x, x') (y, y') = (x + x', max y y')
2445 maxPair (x, x') (y, y') = (max x x', max y y')
2446 libMap = foldCondTree Map.empty
2447 (\(_,v) -> Map.fromListWith sumPair . map (\x -> (x,(1, 1))) $ getModules v )
2448 (Map.unionWith mergePair) -- if a module may occur in nonexclusive branches count it twice strictly and once loosely.
2449 (Map.unionWith maxPair) -- a module occurs the max of times it might appear in exclusive branches
2451 dupLibsStrict = Map.keys $ Map.filter ((>1) . fst) libMap
2452 dupLibsLax = Map.keys $ Map.filter ((>1) . snd) libMap
2453 in if not (null dupLibsLax)
2454 then [PackageBuildImpossible
2455 (DuplicateModule s dupLibsLax)]
2456 else if not (null dupLibsStrict)
2457 then [PackageDistSuspicious
2458 (PotentialDupModule s dupLibsStrict)]
2459 else []
2461 -- ------------------------------------------------------------
2462 -- * Utils
2463 -- ------------------------------------------------------------
2465 quote :: String -> String
2466 quote s = "'" ++ s ++ "'"
2468 commaSep :: [String] -> String
2469 commaSep = intercalate ", "
2471 dups :: Ord a => [a] -> [a]
2472 dups xs = [ x | (x:_:_) <- group (sort xs) ]
2474 fileExtensionSupportedLanguage :: FilePath -> Bool
2475 fileExtensionSupportedLanguage path =
2476 isHaskell || isC
2477 where
2478 extension = takeExtension path
2479 isHaskell = extension `elem` [".hs", ".lhs"]
2480 isC = isJust (filenameCDialect extension)
2482 -- | Whether a path is a good relative path. We aren't worried about perfect
2483 -- cross-platform compatibility here; this function just checks the paths in
2484 -- the (local) @.cabal@ file, while only Hackage needs the portability.
2486 -- >>> let test fp = putStrLn $ show (isGoodRelativeDirectoryPath fp) ++ "; " ++ show (isGoodRelativeFilePath fp)
2488 -- Note that "foo./bar.hs" would be invalid on Windows.
2490 -- >>> traverse_ test ["foo/bar/quu", "a/b.hs", "foo./bar.hs"]
2491 -- Nothing; Nothing
2492 -- Nothing; Nothing
2493 -- Nothing; Nothing
2495 -- Trailing slash is not allowed for files, for directories it is ok.
2497 -- >>> test "foo/"
2498 -- Nothing; Just "trailing slash"
2500 -- Leading @./@ is fine, but @.@ and @./@ are not valid files.
2502 -- >>> traverse_ test [".", "./", "./foo/bar"]
2503 -- Nothing; Just "trailing dot segment"
2504 -- Nothing; Just "trailing slash"
2505 -- Nothing; Nothing
2507 -- Lastly, not good file nor directory cases:
2509 -- >>> traverse_ test ["", "/tmp/src", "foo//bar", "foo/.", "foo/./bar", "foo/../bar"]
2510 -- Just "empty path"; Just "empty path"
2511 -- Just "posix absolute path"; Just "posix absolute path"
2512 -- Just "empty path segment"; Just "empty path segment"
2513 -- Just "trailing same directory segment: ."; Just "trailing same directory segment: ."
2514 -- Just "same directory segment: ."; Just "same directory segment: ."
2515 -- Just "parent directory segment: .."; Just "parent directory segment: .."
2517 -- For the last case, 'isGoodRelativeGlob' doesn't warn:
2519 -- >>> traverse_ (print . isGoodRelativeGlob) ["foo/../bar"]
2520 -- Just "parent directory segment: .."
2522 isGoodRelativeFilePath :: FilePath -> Maybe String
2523 isGoodRelativeFilePath = state0
2524 where
2525 -- initial state
2526 state0 [] = Just "empty path"
2527 state0 (c:cs) | c == '.' = state1 cs
2528 | c == '/' = Just "posix absolute path"
2529 | otherwise = state5 cs
2531 -- after initial .
2532 state1 [] = Just "trailing dot segment"
2533 state1 (c:cs) | c == '.' = state4 cs
2534 | c == '/' = state2 cs
2535 | otherwise = state5 cs
2537 -- after ./ or after / between segments
2538 state2 [] = Just "trailing slash"
2539 state2 (c:cs) | c == '.' = state3 cs
2540 | c == '/' = Just "empty path segment"
2541 | otherwise = state5 cs
2543 -- after non-first segment's .
2544 state3 [] = Just "trailing same directory segment: ."
2545 state3 (c:cs) | c == '.' = state4 cs
2546 | c == '/' = Just "same directory segment: ."
2547 | otherwise = state5 cs
2549 -- after ..
2550 state4 [] = Just "trailing parent directory segment: .."
2551 state4 (c:cs) | c == '.' = state5 cs
2552 | c == '/' = Just "parent directory segment: .."
2553 | otherwise = state5 cs
2555 -- in a segment which is ok.
2556 state5 [] = Nothing
2557 state5 (c:cs) | c == '.' = state5 cs
2558 | c == '/' = state2 cs
2559 | otherwise = state5 cs
2561 -- | See 'isGoodRelativeFilePath'.
2563 -- This is barebones function. We check whether the glob is a valid file
2564 -- by replacing stars @*@ with @x@ses.
2565 isGoodRelativeGlob :: FilePath -> Maybe String
2566 isGoodRelativeGlob = isGoodRelativeFilePath . map f where
2567 f '*' = 'x'
2568 f c = c
2570 -- | See 'isGoodRelativeFilePath'.
2571 isGoodRelativeDirectoryPath :: FilePath -> Maybe String
2572 isGoodRelativeDirectoryPath = state0
2573 where
2574 -- initial state
2575 state0 [] = Just "empty path"
2576 state0 (c:cs) | c == '.' = state5 cs
2577 | c == '/' = Just "posix absolute path"
2578 | otherwise = state4 cs
2580 -- after initial ./ or after / between segments
2581 state1 [] = Nothing
2582 state1 (c:cs) | c == '.' = state2 cs
2583 | c == '/' = Just "empty path segment"
2584 | otherwise = state4 cs
2586 -- after non-first setgment's .
2587 state2 [] = Just "trailing same directory segment: ."
2588 state2 (c:cs) | c == '.' = state3 cs
2589 | c == '/' = Just "same directory segment: ."
2590 | otherwise = state4 cs
2592 -- after ..
2593 state3 [] = Just "trailing parent directory segment: .."
2594 state3 (c:cs) | c == '.' = state4 cs
2595 | c == '/' = Just "parent directory segment: .."
2596 | otherwise = state4 cs
2598 -- in a segment which is ok.
2599 state4 [] = Nothing
2600 state4 (c:cs) | c == '.' = state4 cs
2601 | c == '/' = state1 cs
2602 | otherwise = state4 cs
2604 -- after initial .
2605 state5 [] = Nothing -- "."
2606 state5 (c:cs) | c == '.' = state3 cs
2607 | c == '/' = state1 cs
2608 | otherwise = state4 cs
2610 -- [Note: Good relative paths]
2612 -- Using @kleene@ we can define an extended regex:
2614 -- @
2615 -- import Algebra.Lattice
2616 -- import Kleene
2617 -- import Kleene.ERE (ERE (..), intersections)
2619 -- data C = CDot | CSlash | CChar
2620 -- deriving (Eq, Ord, Enum, Bounded, Show)
2622 -- reservedR :: ERE C
2623 -- reservedR = notChar CSlash
2625 -- pathPieceR :: ERE C
2626 -- pathPieceR = intersections
2627 -- [ plus reservedR
2628 -- , ERENot (string [CDot])
2629 -- , ERENot (string [CDot,CDot])
2630 -- ]
2632 -- filePathR :: ERE C
2633 -- filePathR = optional (string [CDot, CSlash]) <> pathPieceR <> star (char CSlash <> pathPieceR)
2635 -- dirPathR :: ERE C
2636 -- dirPathR = (char CDot \/ filePathR) <> optional (char CSlash)
2638 -- plus :: ERE C -> ERE C
2639 -- plus r = r <> star r
2641 -- optional :: ERE C -> ERE C
2642 -- optional r = mempty \/ r
2643 -- @
2645 -- Results in following state machine for @filePathR@
2647 -- @
2648 -- 0 -> \x -> if
2649 -- | x <= CDot -> 1
2650 -- | otherwise -> 5
2651 -- 1 -> \x -> if
2652 -- | x <= CDot -> 4
2653 -- | x <= CSlash -> 2
2654 -- | otherwise -> 5
2655 -- 2 -> \x -> if
2656 -- | x <= CDot -> 3
2657 -- | otherwise -> 5
2658 -- 3 -> \x -> if
2659 -- | x <= CDot -> 4
2660 -- | otherwise -> 5
2661 -- 4 -> \x -> if
2662 -- | x <= CDot -> 5
2663 -- | otherwise -> 5
2664 -- 5+ -> \x -> if
2665 -- | x <= CDot -> 5
2666 -- | x <= CSlash -> 2
2667 -- | otherwise -> 5
2668 -- @
2670 -- and @dirPathR@:
2672 -- @
2673 -- 0 -> \x -> if
2674 -- | x <= CDot -> 5
2675 -- | otherwise -> 4
2676 -- 1+ -> \x -> if
2677 -- | x <= CDot -> 2
2678 -- | otherwise -> 4
2679 -- 2 -> \x -> if
2680 -- | x <= CDot -> 3
2681 -- | otherwise -> 4
2682 -- 3 -> \x -> if
2683 -- | x <= CDot -> 4
2684 -- | otherwise -> 4
2685 -- 4+ -> \x -> if
2686 -- | x <= CDot -> 4
2687 -- | x <= CSlash -> 1
2688 -- | otherwise -> 4
2689 -- 5+ -> \x -> if
2690 -- | x <= CDot -> 3
2691 -- | x <= CSlash -> 1
2692 -- | otherwise -> 4
2693 -- @
2696 -- TODO: What we really want to do is test if there exists any
2697 -- configuration in which the base version is unbounded above.
2698 -- However that's a bit tricky because there are many possible
2699 -- configurations. As a cheap easy and safe approximation we will
2700 -- pick a single "typical" configuration and check if that has an
2701 -- open upper bound. To get a typical configuration we finalise
2702 -- using no package index and the current platform.
2703 typicalPkg :: GenericPackageDescription
2704 -> Either [Dependency] (PackageDescription, FlagAssignment)
2705 typicalPkg = finalizePD
2706 mempty defaultComponentRequestedSpec (const True)
2707 buildPlatform
2708 (unknownCompilerInfo
2709 (CompilerId buildCompilerFlavor nullVersion)
2710 NoAbiTag)
2713 addConditionalExp :: String -> String
2714 addConditionalExp expl = expl ++
2715 " Alternatively, if you want to use this, make it conditional based "
2716 ++ "on a Cabal configuration flag (with 'manual: True' and 'default: "
2717 ++ "False') and enable that flag during development."