Check.hs 88.4 KB
Newer Older
1 2 3 4
-----------------------------------------------------------------------------
-- |
-- Module      :  Distribution.PackageDescription.Check
-- Copyright   :  Lennart Kolmodin 2008
5
-- License     :  BSD3
6
--
Duncan Coutts's avatar
Duncan Coutts committed
7
-- Maintainer  :  cabal-devel@haskell.org
8 9
-- Portability :  portable
--
Duncan Coutts's avatar
Duncan Coutts committed
10 11 12 13 14 15 16 17
-- This has code for checking for various problems in packages. There is one
-- set of checks that just looks at a 'PackageDescription' in isolation and
-- another set of checks that also looks at files in the package. Some of the
-- checks are basic sanity checks, others are portability standards that we'd
-- like to encourage. There is a 'PackageCheck' type that distinguishes the
-- different kinds of check so we can see which ones are appropriate to report
-- in different situations. This code gets uses when configuring a package when
-- we consider only basic problems. The higher standard is uses when when
Ian D. Bollinger's avatar
Ian D. Bollinger committed
18
-- preparing a source tarball and by Hackage when uploading new packages. The
Duncan Coutts's avatar
Duncan Coutts committed
19 20 21
-- reason for this is that we want to hold packages that are expected to be
-- distributed to a higher standard than packages that are only ever expected
-- to be used on the author's own environment.
22 23 24 25 26

module Distribution.PackageDescription.Check (
        -- * Package Checking
        PackageCheck(..),
        checkPackage,
27
        checkConfiguredPackage,
28 29 30 31 32

        -- ** Checking package contents
        checkPackageFiles,
        checkPackageContent,
        CheckPackageContentOps(..),
33
        checkPackageFileNames,
34 35
  ) where

36 37 38
import Prelude ()
import Distribution.Compat.Prelude

39
import Distribution.PackageDescription
40
import Distribution.PackageDescription.Configuration
Oleg Grenrus's avatar
Oleg Grenrus committed
41
import qualified Distribution.Compat.DList as DList
42 43 44
import Distribution.Compiler
import Distribution.System
import Distribution.License
fmaste's avatar
fmaste committed
45
import Distribution.Simple.BuildPaths (autogenPathsModuleName)
John Ericson's avatar
John Ericson committed
46
import Distribution.Simple.BuildToolDepends
47
import Distribution.Simple.CCompiler
48
import Distribution.Types.ComponentRequestedSpec
49
import Distribution.Types.CondTree
50
import Distribution.Types.Dependency
John Ericson's avatar
John Ericson committed
51
import Distribution.Types.ExeDependency
52
import Distribution.Types.PackageName
53
import Distribution.Types.ExecutableScope
54
import Distribution.Types.UnqualComponentName
55
import Distribution.Simple.Utils hiding (findPackageDesc, notice)
56
import Distribution.Version
57
import Distribution.Package
58
import Distribution.Text
Oleg Grenrus's avatar
Oleg Grenrus committed
59
import Distribution.Utils.Generic (isAscii)
60 61
import Language.Haskell.Extension

62
import Control.Monad (mapM)
63
import qualified Data.ByteString.Lazy as BS
64
import Data.List  (group)
65 66 67 68
import qualified System.Directory as System
         ( doesFileExist, doesDirectoryExist )
import qualified Data.Map as Map

69
import qualified Text.PrettyPrint as Disp
70
import Text.PrettyPrint ((<+>))
71

Oleg Grenrus's avatar
Oleg Grenrus committed
72
import qualified System.Directory (getDirectoryContents)
73
import System.FilePath
74 75
         ( (</>), (<.>), takeExtension, takeFileName, splitDirectories
         , splitPath, splitExtension )
76 77
import System.FilePath.Windows as FilePath.Windows
         ( isValid )
78

Oleg Grenrus's avatar
Oleg Grenrus committed
79
import qualified Data.Set as Set
80

81 82 83 84
import Distribution.Compat.Lens
import qualified Distribution.Types.BuildInfo.Lens as L
import qualified Distribution.Types.PackageDescription.Lens as L
import qualified Distribution.Types.GenericPackageDescription.Lens as L
85

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
-- | Results of some kind of failed package check.
--
-- There are a range of severities, from merely dubious to totally insane.
-- All of them come with a human readable explanation. In future we may augment
-- them with more machine readable explanations, for example to help an IDE
-- suggest automatic corrections.
--
data PackageCheck =

       -- | This package description is no good. There's no way it's going to
       -- build sensibly. This should give an error at configure time.
       PackageBuildImpossible { explanation :: String }

       -- | A problem that is likely to affect building the package, or an
       -- issue that we'd like every package author to be aware of, even if
       -- the package is never distributed.
     | PackageBuildWarning { explanation :: String }

       -- | An issue that might not be a problem for the package author but
Ian D. Bollinger's avatar
Ian D. Bollinger committed
105
       -- might be annoying or detrimental when the package is distributed to
106 107 108 109 110
       -- users. We should encourage distributed packages to be free from these
       -- issues, but occasionally there are justifiable reasons so we cannot
       -- ban them entirely.
     | PackageDistSuspicious { explanation :: String }

111
       -- | Like PackageDistSuspicious but will only display warnings
112
       -- rather than causing abnormal exit when you run 'cabal check'.
113 114
     | PackageDistSuspiciousWarn { explanation :: String }

Ian D. Bollinger's avatar
Ian D. Bollinger committed
115
       -- | An issue that is OK in the author's environment but is almost
116 117 118 119
       -- certain to be a portability problem for other environments. We can
       -- quite legitimately refuse to publicly distribute packages with these
       -- problems.
     | PackageDistInexcusable { explanation :: String }
120
  deriving (Eq)
121 122 123 124 125 126 127 128

instance Show PackageCheck where
    show notice = explanation notice

check :: Bool -> PackageCheck -> Maybe PackageCheck
check False _  = Nothing
check True  pc = Just pc

Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
129 130
checkSpecVersion :: PackageDescription -> [Int] -> Bool -> PackageCheck
                 -> Maybe PackageCheck
131
checkSpecVersion pkg specver cond pc
132
  | specVersion pkg >= mkVersion specver  = Nothing
133 134
  | otherwise                             = check cond pc

135 136 137 138 139 140
-- ------------------------------------------------------------
-- * Standard checks
-- ------------------------------------------------------------

-- | Check for common mistakes and problems in package descriptions.
--
Ian D. Bollinger's avatar
Ian D. Bollinger committed
141
-- This is the standard collection of checks covering all aspects except
142 143 144
-- for checks that require looking at files within the package. For those
-- see 'checkPackageFiles'.
--
145 146 147 148 149 150 151 152 153
-- It requires the 'GenericPackageDescription' and optionally a particular
-- configuration of that package. If you pass 'Nothing' then we just check
-- a version of the generic description using 'flattenPackageDescription'.
--
checkPackage :: GenericPackageDescription
             -> Maybe PackageDescription
             -> [PackageCheck]
checkPackage gpkg mpkg =
     checkConfiguredPackage pkg
154
  ++ checkConditionals gpkg
155
  ++ checkPackageVersions gpkg
156
  ++ checkDevelopmentOnlyFlags gpkg
Oleg Grenrus's avatar
Oleg Grenrus committed
157
  ++ checkFlagNames gpkg
Oleg Grenrus's avatar
Oleg Grenrus committed
158
  ++ checkUnusedFlags gpkg
Oleg Grenrus's avatar
Oleg Grenrus committed
159
  ++ checkUnicodeXFields gpkg
160 161 162 163
  where
    pkg = fromMaybe (flattenPackageDescription gpkg) mpkg

--TODO: make this variant go away
refold's avatar
Typo.  
refold committed
164
--      we should always know the GenericPackageDescription
165 166
checkConfiguredPackage :: PackageDescription -> [PackageCheck]
checkConfiguredPackage pkg =
167 168 169
    checkSanity pkg
 ++ checkFields pkg
 ++ checkLicense pkg
170
 ++ checkSourceRepos pkg
171
 ++ checkGhcOptions pkg
Duncan Coutts's avatar
Duncan Coutts committed
172
 ++ checkCCOptions pkg
173
 ++ checkCPPOptions pkg
174
 ++ checkPaths pkg
175
 ++ checkCabalVersion pkg
176 177 178 179 180 181 182 183 184 185 186 187


-- ------------------------------------------------------------
-- * Basic sanity checks
-- ------------------------------------------------------------

-- | Check that this package description is sane.
--
checkSanity :: PackageDescription -> [PackageCheck]
checkSanity pkg =
  catMaybes [

188
    check (null . unPackageName . packageName $ pkg) $
189 190
      PackageBuildImpossible "No 'name' field."

191
  , check (nullVersion == packageVersion pkg) $
192 193
      PackageBuildImpossible "No 'version' field."

194 195 196
  , check (all ($ pkg) [ null . executables
                       , null . testSuites
                       , null . benchmarks
197 198
                       , null . allLibraries
                       , null . foreignLibs ]) $
199
      PackageBuildImpossible
200
        "No executables, libraries, tests, or benchmarks found. Nothing to do."
201

202 203 204 205
  , check (any isNothing (map libName $ subLibraries pkg)) $
      PackageBuildImpossible $ "Found one or more unnamed internal libraries. "
        ++ "Only the non-internal library can have the same name as the package."

tibbe's avatar
tibbe committed
206
  , check (not (null duplicateNames)) $
207 208
      PackageBuildImpossible $ "Duplicate sections: "
        ++ commaSep (map unUnqualComponentName duplicateNames)
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
209 210
        ++ ". The name of every library, executable, test suite,"
        ++ " and benchmark section in"
tibbe's avatar
tibbe committed
211
        ++ " the package must be unique."
212 213

  -- NB: but it's OK for executables to have the same name!
214 215
  -- TODO shouldn't need to compare on the string level
  , check (any (== display (packageName pkg)) (display <$> subLibNames)) $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
216 217 218 219 220
      PackageBuildImpossible $ "Illegal internal library name "
        ++ display (packageName pkg)
        ++ ". Internal libraries cannot have the same name as the package."
        ++ " Maybe you wanted a non-internal library?"
        ++ " If so, rewrite the section stanza"
221
        ++ " from 'library: '" ++ display (packageName pkg) ++ "' to 'library'."
222
  ]
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
223 224
  --TODO: check for name clashes case insensitively: windows file systems cannot
  --cope.
225

226
  ++ concatMap (checkLibrary    pkg) (allLibraries pkg)
227 228 229
  ++ concatMap (checkExecutable pkg) (executables pkg)
  ++ concatMap (checkTestSuite  pkg) (testSuites pkg)
  ++ concatMap (checkBenchmark  pkg) (benchmarks pkg)
230 231 232

  ++ catMaybes [

233
    check (specVersion pkg > cabalVersion) $
234
      PackageBuildImpossible $
235 236 237
           "This package description follows version "
        ++ display (specVersion pkg) ++ " of the Cabal specification. This "
        ++ "tool only supports up to version " ++ display cabalVersion ++ "."
238
  ]
239
  where
240
    -- The public 'library' gets special dispensation, because it
241
    -- is common practice to export a library and name the executable
242 243
    -- the same as the package.
    subLibNames = catMaybes . map libName $ subLibraries pkg
244
    exeNames = map exeName $ executables pkg
245
    testNames = map testName $ testSuites pkg
tibbe's avatar
tibbe committed
246
    bmNames = map benchmarkName $ benchmarks pkg
247
    duplicateNames = dups $ subLibNames ++ exeNames ++ testNames ++ bmNames
248

249
checkLibrary :: PackageDescription -> Library -> [PackageCheck]
250
checkLibrary pkg lib =
251 252
  catMaybes [

253
    check (not (null moduleDuplicates)) $
254
       PackageBuildImpossible $
255 256
            "Duplicate modules in library: "
         ++ commaSep (map display moduleDuplicates)
257

258 259
  -- TODO: This check is bogus if a required-signature was passed through
  , check (null (explicitLibModules lib) && null (reexportedModules lib)) $
260
      PackageDistSuspiciousWarn $
261 262
           "Library " ++ (case libName lib of
                            Nothing -> ""
263
                            Just n -> display n
264
                            ) ++ "does not expose any modules"
265

266 267
    -- check use of signatures sections
  , checkVersion [1,25] (not (null (signatures lib))) $
268
      PackageDistInexcusable $
269 270
           "To use the 'signatures' field the package needs to specify "
        ++ "at least 'cabal-version: >= 1.25'."
fmaste's avatar
fmaste committed
271 272 273

    -- check that all autogen-modules appear on other-modules or exposed-modules
  , check
274
      (not $ and $ map (flip elem (explicitLibModules lib)) (libModulesAutogen lib)) $
fmaste's avatar
fmaste committed
275 276 277 278
      PackageBuildImpossible $
           "An 'autogen-module' is neither on 'exposed-modules' or "
        ++ "'other-modules'."

279 280
  ]

281
  where
282 283
    checkVersion :: [Int] -> Bool -> PackageCheck -> Maybe PackageCheck
    checkVersion ver cond pc
284
      | specVersion pkg >= mkVersion ver       = Nothing
285 286
      | otherwise                              = check cond pc

287 288
    -- TODO: not sure if this check is always right in Backpack
    moduleDuplicates = dups (explicitLibModules lib ++
289
                             map moduleReexportName (reexportedModules lib))
Duncan Coutts's avatar
Duncan Coutts committed
290

291 292
checkExecutable :: PackageDescription -> Executable -> [PackageCheck]
checkExecutable pkg exe =
293 294 295 296
  catMaybes [

    check (null (modulePath exe)) $
      PackageBuildImpossible $
297
        "No 'main-is' field found for executable " ++ display (exeName exe)
298 299

  , check (not (null (modulePath exe))
300
       && (not $ fileExtensionSupportedLanguage $ modulePath exe)) $
301
      PackageBuildImpossible $
302 303 304
           "The 'main-is' field must specify a '.hs' or '.lhs' file "
        ++ "(even if it is generated by a preprocessor), "
        ++ "or it may specify a C/C++/obj-C source file."
Duncan Coutts's avatar
Duncan Coutts committed
305

306 307 308 309 310 311 312
  , checkSpecVersion pkg [1,17]
          (fileExtensionSupportedLanguage (modulePath exe)
        && takeExtension (modulePath exe) `notElem` [".hs", ".lhs"]) $
      PackageDistInexcusable $
           "The package uses a C/C++/obj-C source file for the 'main-is' field. "
        ++ "To use this feature you must specify 'cabal-version: >= 1.18'."

Duncan Coutts's avatar
Duncan Coutts committed
313
  , check (not (null moduleDuplicates)) $
314
       PackageBuildImpossible $
315
            "Duplicate modules in executable '" ++ display (exeName exe) ++ "': "
316
         ++ commaSep (map display moduleDuplicates)
fmaste's avatar
fmaste committed
317 318 319 320 321

    -- check that all autogen-modules appear on other-modules
  , check
      (not $ and $ map (flip elem (exeModules exe)) (exeModulesAutogen exe)) $
      PackageBuildImpossible $
322
           "On executable '" ++ display (exeName exe) ++ "' an 'autogen-module' is not "
fmaste's avatar
fmaste committed
323 324
        ++ "on 'other-modules'"

325 326
  , checkSpecVersion pkg [2,0] (exeScope exe /= ExecutableScopeUnknown) $
      PackageDistSuspiciousWarn $
327
           "To use the 'scope' field the package needs to specify "
328
        ++ "at least 'cabal-version: >= 2.0'."
329

330
  ]
331 332
  where
    moduleDuplicates = dups (exeModules exe)
333

334 335
checkTestSuite :: PackageDescription -> TestSuite -> [PackageCheck]
checkTestSuite pkg test =
336 337
  catMaybes [

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    case testInterface test of
      TestSuiteUnsupported tt@(TestTypeUnknown _ _) -> Just $
        PackageBuildWarning $
             quote (display tt) ++ " is not a known type of test suite. "
          ++ "The known test suite types are: "
          ++ commaSep (map display knownTestTypes)

      TestSuiteUnsupported tt -> Just $
        PackageBuildWarning $
             quote (display tt) ++ " is not a supported test suite version. "
          ++ "The known test suite types are: "
          ++ commaSep (map display knownTestTypes)
      _ -> Nothing

  , check (not $ null moduleDuplicates) $
353
      PackageBuildImpossible $
354
           "Duplicate modules in test suite '" ++ display (testName test) ++ "': "
355
        ++ commaSep (map display moduleDuplicates)
356

357 358 359
  , check mainIsWrongExt $
      PackageBuildImpossible $
           "The 'main-is' field must specify a '.hs' or '.lhs' file "
360 361
        ++ "(even if it is generated by a preprocessor), "
        ++ "or it may specify a C/C++/obj-C source file."
362

363 364 365 366
  , checkSpecVersion pkg [1,17] (mainIsNotHsExt && not mainIsWrongExt) $
      PackageDistInexcusable $
           "The package uses a C/C++/obj-C source file for the 'main-is' field. "
        ++ "To use this feature you must specify 'cabal-version: >= 1.18'."
fmaste's avatar
fmaste committed
367 368 369 370 371 372 373 374

    -- check that all autogen-modules appear on other-modules
  , check
      (not $ and $ map
        (flip elem (testModules test))
        (testModulesAutogen test)
      ) $
      PackageBuildImpossible $
375
           "On test suite '" ++ display (testName test) ++ "' an 'autogen-module' is not "
fmaste's avatar
fmaste committed
376
        ++ "on 'other-modules'"
377
  ]
378 379
  where
    moduleDuplicates = dups $ testModules test
380 381

    mainIsWrongExt = case testInterface test of
382
      TestSuiteExeV10 _ f -> not $ fileExtensionSupportedLanguage f
383 384
      _                   -> False

385 386 387 388
    mainIsNotHsExt = case testInterface test of
      TestSuiteExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]
      _                   -> False

tibbe's avatar
tibbe committed
389
checkBenchmark :: PackageDescription -> Benchmark -> [PackageCheck]
390
checkBenchmark _pkg bm =
tibbe's avatar
tibbe committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
  catMaybes [

    case benchmarkInterface bm of
      BenchmarkUnsupported tt@(BenchmarkTypeUnknown _ _) -> Just $
        PackageBuildWarning $
             quote (display tt) ++ " is not a known type of benchmark. "
          ++ "The known benchmark types are: "
          ++ commaSep (map display knownBenchmarkTypes)

      BenchmarkUnsupported tt -> Just $
        PackageBuildWarning $
             quote (display tt) ++ " is not a supported benchmark version. "
          ++ "The known benchmark types are: "
          ++ commaSep (map display knownBenchmarkTypes)
      _ -> Nothing

  , check (not $ null moduleDuplicates) $
408
      PackageBuildImpossible $
409
           "Duplicate modules in benchmark '" ++ display (benchmarkName bm) ++ "': "
tibbe's avatar
tibbe committed
410 411 412 413 414 415
        ++ commaSep (map display moduleDuplicates)

  , check mainIsWrongExt $
      PackageBuildImpossible $
           "The 'main-is' field must specify a '.hs' or '.lhs' file "
        ++ "(even if it is generated by a preprocessor)."
fmaste's avatar
fmaste committed
416 417 418 419 420 421 422 423

    -- check that all autogen-modules appear on other-modules
  , check
      (not $ and $ map
        (flip elem (benchmarkModules bm))
        (benchmarkModulesAutogen bm)
      ) $
      PackageBuildImpossible $
424
             "On benchmark '" ++ display (benchmarkName bm) ++ "' an 'autogen-module' is "
fmaste's avatar
fmaste committed
425
          ++ "not on 'other-modules'"
tibbe's avatar
tibbe committed
426 427 428 429 430 431 432 433
  ]
  where
    moduleDuplicates = dups $ benchmarkModules bm

    mainIsWrongExt = case benchmarkInterface bm of
      BenchmarkExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]
      _                   -> False

434 435 436 437 438 439 440 441
-- ------------------------------------------------------------
-- * Additional pure checks
-- ------------------------------------------------------------

checkFields :: PackageDescription -> [PackageCheck]
checkFields pkg =
  catMaybes [

442 443 444 445 446 447 448
    check (not . FilePath.Windows.isValid . display . packageName $ pkg) $
      PackageDistInexcusable $
           "Unfortunately, the package name '" ++ display (packageName pkg)
        ++ "' is one of the reserved system file names on Windows. Many tools "
        ++ "need to convert package names to file names so using this name "
        ++ "would cause problems."

449 450 451 452 453
  , check ((isPrefixOf "z-") . display . packageName $ pkg) $
      PackageDistInexcusable $
           "Package names with the prefix 'z-' are reserved by Cabal and "
        ++ "cannot be used."

454
  , check (isNothing (buildType pkg)) $
455 456 457 458 459 460 461 462 463
      PackageBuildWarning $
           "No 'build-type' specified. If you do not need a custom Setup.hs or "
        ++ "./configure script then use 'build-type: Simple'."

  , case buildType pkg of
      Just (UnknownBuildType unknown) -> Just $
        PackageBuildWarning $
             quote unknown ++ " is not a known 'build-type'. "
          ++ "The known build types are: "
464
          ++ commaSep (map display knownBuildTypes)
465 466
      _ -> Nothing

467 468 469 470 471 472
  , check (isJust (setupBuildInfo pkg) && buildType pkg /= Just Custom) $
      PackageBuildWarning $
           "Ignoring the 'custom-setup' section because the 'build-type' is "
        ++ "not 'Custom'. Use 'build-type: Custom' if you need to use a "
        ++ "custom Setup.hs script."

473 474
  , check (not (null unknownCompilers)) $
      PackageBuildWarning $
475
        "Unknown compiler " ++ commaSep (map quote unknownCompilers)
476 477
                            ++ " in 'tested-with' field."

478 479 480 481
  , check (not (null unknownLanguages)) $
      PackageBuildWarning $
        "Unknown languages: " ++ commaSep unknownLanguages

482 483
  , check (not (null unknownExtensions)) $
      PackageBuildWarning $
484
        "Unknown extensions: " ++ commaSep unknownExtensions
485

486 487 488 489 490 491 492
  , check (not (null languagesUsedAsExtensions)) $
      PackageBuildWarning $
           "Languages listed as extensions: "
        ++ commaSep languagesUsedAsExtensions
        ++ ". Languages must be specified in either the 'default-language' "
        ++ " or the 'other-languages' field."

493
  , check (not (null ourDeprecatedExtensions)) $
494 495
      PackageDistSuspicious $
           "Deprecated extensions: "
496
        ++ commaSep (map (quote . display . fst) ourDeprecatedExtensions)
EyalLotem's avatar
EyalLotem committed
497
        ++ ". " ++ unwords
498 499
             [ "Instead of '" ++ display ext
            ++ "' use '" ++ display replacement ++ "'."
500
             | (ext, Just replacement) <- ourDeprecatedExtensions ]
501

502 503 504 505 506 507
  , check (null (category pkg)) $
      PackageDistSuspicious "No 'category' field."

  , check (null (maintainer pkg)) $
      PackageDistSuspicious "No 'maintainer' field."

508
  , check (null (synopsis pkg) && null (description pkg)) $
EyalLotem's avatar
EyalLotem committed
509
      PackageDistInexcusable "No 'synopsis' or 'description' field."
510 511 512 513 514

  , check (null (description pkg) && not (null (synopsis pkg))) $
      PackageDistSuspicious "No 'description' field."

  , check (null (synopsis pkg) && not (null (description pkg))) $
515 516
      PackageDistSuspicious "No 'synopsis' field."

Ian D. Bollinger's avatar
Ian D. Bollinger committed
517
    --TODO: recommend the bug reports URL, author and homepage fields
518 519 520
    --TODO: recommend not using the stability field
    --TODO: recommend specifying a source repo

521 522 523
  , check (length (synopsis pkg) >= 80) $
      PackageDistSuspicious
        "The 'synopsis' field is rather long (max 80 chars is recommended)."
524

525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
    -- See also https://github.com/haskell/cabal/pull/3479
  , check (not (null (description pkg))
           && length (description pkg) <= length (synopsis pkg)) $
      PackageDistSuspicious $
           "The 'description' field should be longer than the 'synopsis' "
        ++ "field. "
        ++ "It's useful to provide an informative 'description' to allow "
        ++ "Haskell programmers who have never heard about your package to "
        ++ "understand the purpose of your package. "
        ++ "The 'description' field content is typically shown by tooling "
        ++ "(e.g. 'cabal info', Haddock, Hackage) below the 'synopsis' which "
        ++ "serves as a headline. "
        ++ "Please refer to <https://www.haskell.org/"
        ++ "cabal/users-guide/developing-packages.html#package-properties>"
        ++ " for more details."

541 542 543 544 545 546 547 548 549
    -- check use of impossible constraints "tested-with: GHC== 6.10 && ==6.12"
  , check (not (null testedWithImpossibleRanges)) $
      PackageDistInexcusable $
           "Invalid 'tested-with' version range: "
        ++ commaSep (map display testedWithImpossibleRanges)
        ++ ". To indicate that you have tested a package with multiple "
        ++ "different versions of the same compiler use multiple entries, "
        ++ "for example 'tested-with: GHC==6.10.4, GHC==6.12.3' and not "
        ++ "'tested-with: GHC==6.10.4 && ==6.12.3'."
550

John Ericson's avatar
John Ericson committed
551
  , check (not (null depInternalLibraryWithExtraVersion)) $
552
      PackageBuildWarning $
John Ericson's avatar
John Ericson committed
553
           "The package has an extraneous version range for a dependency on an "
554
        ++ "internal library: "
John Ericson's avatar
John Ericson committed
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
        ++ commaSep (map display depInternalLibraryWithExtraVersion)
        ++ ". This version range includes the current package but isn't needed "
        ++ "as the current package's library will always be used."

  , check (not (null depInternalLibraryWithImpossibleVersion)) $
      PackageBuildImpossible $
           "The package has an impossible version range for a dependency on an "
        ++ "internal library: "
        ++ commaSep (map display depInternalLibraryWithImpossibleVersion)
        ++ ". This version range does not include the current package, and must "
        ++ "be removed as the current package's library will always be used."

  , check (not (null depInternalExecutableWithExtraVersion)) $
      PackageBuildWarning $
           "The package has an extraneous version range for a dependency on an "
        ++ "internal executable: "
        ++ commaSep (map display depInternalExecutableWithExtraVersion)
        ++ ". This version range includes the current package but isn't needed "
        ++ "as the current package's executable will always be used."

  , check (not (null depInternalExecutableWithImpossibleVersion)) $
      PackageBuildImpossible $
           "The package has an impossible version range for a dependency on an "
        ++ "internal executable: "
        ++ commaSep (map display depInternalExecutableWithImpossibleVersion)
        ++ ". This version range does not include the current package, and must "
        ++ "be removed as the current package's executable will always be used."

  , check (not (null depMissingInternalExecutable)) $
      PackageBuildImpossible $
           "The package depends on a missing internal executable: "
        ++ commaSep (map display depInternalExecutableWithImpossibleVersion)
587
  ]
588 589
  where
    unknownCompilers  = [ name | (OtherCompiler name, _) <- testedWith pkg ]
590 591
    unknownLanguages  = [ name | bi <- allBuildInfo pkg
                               , UnknownLanguage name <- allLanguages bi ]
592
    unknownExtensions = [ name | bi <- allBuildInfo pkg
593 594
                               , UnknownExtension name <- allExtensions bi
                               , name `notElem` map display knownLanguages ]
595 596
    ourDeprecatedExtensions = nub $ catMaybes
      [ find ((==ext) . fst) deprecatedExtensions
597
      | bi <- allBuildInfo pkg
Ian Lynagh's avatar
Ian Lynagh committed
598
      , ext <- allExtensions bi ]
599 600 601 602
    languagesUsedAsExtensions =
      [ name | bi <- allBuildInfo pkg
             , UnknownExtension name <- allExtensions bi
             , name `elem` map display knownLanguages ]
603

604
    testedWithImpossibleRanges =
605
      [ Dependency (mkPackageName (display compiler)) vr
606 607 608
      | (compiler, vr) <- testedWith pkg
      , isNoVersion vr ]

609
    internalLibraries =
610
        map (maybe (packageName pkg) (unqualComponentNameToPackageName) . libName)
611
            (allLibraries pkg)
John Ericson's avatar
John Ericson committed
612 613 614 615

    internalExecutables = map exeName $ executables pkg

    internalLibDeps =
616 617
      [ dep
      | bi <- allBuildInfo pkg
John Ericson's avatar
John Ericson committed
618
      , dep@(Dependency name _) <- targetBuildDepends bi
619 620 621
      , name `elem` internalLibraries
      ]

John Ericson's avatar
John Ericson committed
622 623 624
    internalExeDeps =
      [ dep
      | bi <- allBuildInfo pkg
625 626
      , dep <- getAllToolDependencies pkg bi
      , isInternal pkg dep
John Ericson's avatar
John Ericson committed
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
      ]

    depInternalLibraryWithExtraVersion =
      [ dep
      | dep@(Dependency _ versionRange) <- internalLibDeps
      , not $ isAnyVersion versionRange
      , packageVersion pkg `withinRange` versionRange
      ]

    depInternalLibraryWithImpossibleVersion =
      [ dep
      | dep@(Dependency _ versionRange) <- internalLibDeps
      , not $ packageVersion pkg `withinRange` versionRange
      ]

    depInternalExecutableWithExtraVersion =
      [ dep
      | dep@(ExeDependency _ _ versionRange) <- internalExeDeps
      , not $ isAnyVersion versionRange
      , packageVersion pkg `withinRange` versionRange
      ]

    depInternalExecutableWithImpossibleVersion =
      [ dep
      | dep@(ExeDependency _ _ versionRange) <- internalExeDeps
      , not $ packageVersion pkg `withinRange` versionRange
      ]

    depMissingInternalExecutable =
      [ dep
      | dep@(ExeDependency _ eName _) <- internalExeDeps
      , not $ eName `elem` internalExecutables
      ]

661

662 663 664 665
checkLicense :: PackageDescription -> [PackageCheck]
checkLicense pkg =
  catMaybes [

666
    check (license pkg == UnspecifiedLicense) $
667
      PackageDistInexcusable
668
        "The 'license' field is missing."
669

670 671 672
  , check (license pkg == AllRightsReserved) $
      PackageDistSuspicious
        "The 'license' is AllRightsReserved. Is that really what you want?"
673 674 675
  , case license pkg of
      UnknownLicense l -> Just $
        PackageBuildWarning $
676 677 678
             quote ("license: " ++ l) ++ " is not a recognised license. The "
          ++ "known licenses are: "
          ++ commaSep (map display knownLicenses)
679 680
      _ -> Nothing

681 682 683
  , check (license pkg == BSD4) $
      PackageDistSuspicious $
           "Using 'license: BSD4' is almost always a misunderstanding. 'BSD4' "
684 685
        ++ "refers to the old 4-clause BSD license with the advertising "
        ++ "clause. 'BSD3' refers the new 3-clause BSD license."
686

687 688 689 690 691 692 693 694 695 696
  , case unknownLicenseVersion (license pkg) of
      Just knownVersions -> Just $
        PackageDistSuspicious $
             "'license: " ++ display (license pkg) ++ "' is not a known "
          ++ "version of that license. The known versions are "
          ++ commaSep (map display knownVersions)
          ++ ". If this is not a mistake and you think it should be a known "
          ++ "version then please file a ticket."
      _ -> Nothing

697 698
  , check (license pkg `notElem` [ AllRightsReserved
                                 , UnspecifiedLicense, PublicDomain]
699
           -- AllRightsReserved and PublicDomain are not strictly
700
           -- licenses so don't need license files.
Duncan Coutts's avatar
Duncan Coutts committed
701
        && null (licenseFiles pkg)) $
702 703
      PackageDistSuspicious "A 'license-file' is not specified."
  ]
704 705 706 707 708 709 710
  where
    unknownLicenseVersion (GPL  (Just v))
      | v `notElem` knownVersions = Just knownVersions
      where knownVersions = [ v' | GPL  (Just v') <- knownLicenses ]
    unknownLicenseVersion (LGPL (Just v))
      | v `notElem` knownVersions = Just knownVersions
      where knownVersions = [ v' | LGPL (Just v') <- knownLicenses ]
711 712 713
    unknownLicenseVersion (AGPL (Just v))
      | v `notElem` knownVersions = Just knownVersions
      where knownVersions = [ v' | AGPL (Just v') <- knownLicenses ]
714 715 716
    unknownLicenseVersion (Apache  (Just v))
      | v `notElem` knownVersions = Just knownVersions
      where knownVersions = [ v' | Apache  (Just v') <- knownLicenses ]
717
    unknownLicenseVersion _ = Nothing
718

719 720 721 722 723 724 725 726 727 728
checkSourceRepos :: PackageDescription -> [PackageCheck]
checkSourceRepos pkg =
  catMaybes $ concat [[

    case repoKind repo of
      RepoKindUnknown kind -> Just $ PackageDistInexcusable $
        quote kind ++ " is not a recognised kind of source-repository. "
                   ++ "The repo kind is usually 'head' or 'this'"
      _ -> Nothing

EyalLotem's avatar
EyalLotem committed
729
  , check (isNothing (repoType repo)) $
730 731 732
      PackageDistInexcusable
        "The source-repository 'type' is a required field."

EyalLotem's avatar
EyalLotem committed
733
  , check (isNothing (repoLocation repo)) $
734 735 736
      PackageDistInexcusable
        "The source-repository 'location' is a required field."

EyalLotem's avatar
EyalLotem committed
737
  , check (repoType repo == Just CVS && isNothing (repoModule repo)) $
738 739 740
      PackageDistInexcusable
        "For a CVS source-repository, the 'module' is a required field."

EyalLotem's avatar
EyalLotem committed
741
  , check (repoKind repo == RepoThis && isNothing (repoTag repo)) $
742 743 744 745 746
      PackageDistInexcusable $
           "For the 'this' kind of source-repository, the 'tag' is a required "
        ++ "field. It should specify the tag corresponding to this version "
        ++ "or release of the package."

747
  , check (maybe False isAbsoluteOnAnyPlatform (repoSubdir repo)) $
748 749 750 751 752 753 754
      PackageDistInexcusable
        "The 'subdir' field of a source-repository must be a relative path."
  ]
  | repo <- sourceRepos pkg ]

--TODO: check location looks like a URL for some repo types.

755 756 757 758
checkGhcOptions :: PackageDescription -> [PackageCheck]
checkGhcOptions pkg =
  catMaybes [

759
    checkFlags ["-fasm"] $
760
      PackageDistInexcusable $
761 762
           "'ghc-options: -fasm' is unnecessary and will not work on CPU "
        ++ "architectures other than x86, x86-64, ppc or sparc."
763

Duncan Coutts's avatar
Duncan Coutts committed
764 765
  , checkFlags ["-fvia-C"] $
      PackageDistSuspicious $
766 767 768 769
           "'ghc-options: -fvia-C' is usually unnecessary. If your package "
        ++ "needs -via-C for correctness rather than performance then it "
        ++ "is using the FFI incorrectly and will probably not work with GHC "
        ++ "6.10 or later."
Duncan Coutts's avatar
Duncan Coutts committed
770 771 772

  , checkFlags ["-fhpc"] $
      PackageDistInexcusable $
773 774
           "'ghc-options: -fhpc' is not not necessary. Use the configure flag "
        ++ " --enable-coverage instead."
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
775

Duncan Coutts's avatar
Duncan Coutts committed
776
  , checkFlags ["-prof"] $
777 778 779
      PackageBuildWarning $
           "'ghc-options: -prof' is not necessary and will lead to problems "
        ++ "when used on a library. Use the configure flag "
gershomb's avatar
gershomb committed
780
        ++ "--enable-library-profiling and/or --enable-profiling."
Duncan Coutts's avatar
Duncan Coutts committed
781 782

  , checkFlags ["-o"] $
783
      PackageBuildWarning $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
784 785
           "'ghc-options: -o' is not needed. "
        ++ "The output files are named automatically."
Duncan Coutts's avatar
Duncan Coutts committed
786 787

  , checkFlags ["-hide-package"] $
788
      PackageBuildWarning $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
789 790
      "'ghc-options: -hide-package' is never needed. "
      ++ "Cabal hides all packages."
Duncan Coutts's avatar
Duncan Coutts committed
791

Duncan Coutts's avatar
Duncan Coutts committed
792
  , checkFlags ["--make"] $
793
      PackageBuildWarning $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
794
      "'ghc-options: --make' is never needed. Cabal uses this automatically."
Duncan Coutts's avatar
Duncan Coutts committed
795

Duncan Coutts's avatar
Duncan Coutts committed
796 797
  , checkFlags ["-main-is"] $
      PackageDistSuspicious $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
798
      "'ghc-options: -main-is' is not portable."
Duncan Coutts's avatar
Duncan Coutts committed
799 800

  , checkFlags ["-O0", "-Onot"] $
801
      PackageDistSuspicious $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
802 803
      "'ghc-options: -O0' is not needed. "
      ++ "Use the --disable-optimization configure flag."
Duncan Coutts's avatar
Duncan Coutts committed
804 805

  , checkFlags [ "-O", "-O1"] $
806
      PackageDistInexcusable $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
807 808 809
      "'ghc-options: -O' is not needed. "
      ++ "Cabal automatically adds the '-O' flag. "
      ++ "Setting it yourself interferes with the --disable-optimization flag."
810

Duncan Coutts's avatar
Duncan Coutts committed
811
  , checkFlags ["-O2"] $
812
      PackageDistSuspiciousWarn $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
813 814 815
      "'ghc-options: -O2' is rarely needed. "
      ++ "Check that it is giving a real benefit "
      ++ "and not just imposing longer compile times on your users."
816

Duncan Coutts's avatar
Duncan Coutts committed
817
  , checkFlags ["-split-objs"] $
818
      PackageBuildWarning $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
819 820
        "'ghc-options: -split-objs' is not needed. "
        ++ "Use the --enable-split-objs configure flag."
Duncan Coutts's avatar
Duncan Coutts committed
821

822
  , checkFlags ["-optl-Wl,-s", "-optl-s"] $
823
      PackageDistInexcusable $
824 825 826 827 828 829
           "'ghc-options: -optl-Wl,-s' is not needed and is not portable to all"
        ++ " operating systems. Cabal 1.4 and later automatically strip"
        ++ " executables. Cabal also has a flag --disable-executable-stripping"
        ++ " which is necessary when building packages for some Linux"
        ++ " distributions and using '-optl-Wl,-s' prevents that from working."

Duncan Coutts's avatar
Duncan Coutts committed
830 831
  , checkFlags ["-fglasgow-exts"] $
      PackageDistSuspicious $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
832 833
        "Instead of 'ghc-options: -fglasgow-exts' it is preferable to use "
        ++ "the 'extensions' field."
Duncan Coutts's avatar
Duncan Coutts committed
834

835
  , check ("-threaded" `elem` lib_ghc_options) $
836
      PackageBuildWarning $
837 838 839
           "'ghc-options: -threaded' has no effect for libraries. It should "
        ++ "only be used for executables."

840 841 842 843 844 845 846 847 848 849
  , check ("-rtsopts" `elem` lib_ghc_options) $
      PackageBuildWarning $
           "'ghc-options: -rtsopts' has no effect for libraries. It should "
        ++ "only be used for executables."

  , check (any (\opt -> "-with-rtsopts" `isPrefixOf` opt) lib_ghc_options) $
      PackageBuildWarning $
           "'ghc-options: -with-rtsopts' has no effect for libraries. It "
        ++ "should only be used for executables."

Duncan Coutts's avatar
Duncan Coutts committed
850
  , checkAlternatives "ghc-options" "extensions"
851 852
      [ (flag, display extension) | flag <- all_ghc_options
                                  , Just extension <- [ghcExtension flag] ]
Duncan Coutts's avatar
Duncan Coutts committed
853 854

  , checkAlternatives "ghc-options" "extensions"
855
      [ (flag, extension) | flag@('-':'X':extension) <- all_ghc_options ]
Duncan Coutts's avatar
Duncan Coutts committed
856 857 858 859 860 861 862 863 864 865 866 867 868

  , checkAlternatives "ghc-options" "cpp-options" $
         [ (flag, flag) | flag@('-':'D':_) <- all_ghc_options ]
      ++ [ (flag, flag) | flag@('-':'U':_) <- all_ghc_options ]

  , checkAlternatives "ghc-options" "include-dirs"
      [ (flag, dir) | flag@('-':'I':dir) <- all_ghc_options ]

  , checkAlternatives "ghc-options" "extra-libraries"
      [ (flag, lib) | flag@('-':'l':lib) <- all_ghc_options ]

  , checkAlternatives "ghc-options" "extra-lib-dirs"
      [ (flag, dir) | flag@('-':'L':dir) <- all_ghc_options ]
869 870 871 872 873 874 875 876

  , checkAlternatives "ghc-options" "frameworks"
      [ (flag, fmwk) | (flag@"-framework", fmwk) <-
           zip all_ghc_options (safeTail all_ghc_options) ]

  , checkAlternatives "ghc-options" "extra-framework-dirs"
      [ (flag, dir) | (flag@"-framework-path", dir) <-
           zip all_ghc_options (safeTail all_ghc_options) ]
877 878 879
  ]

  where
880
    all_ghc_options    = concatMap get_ghc_options (allBuildInfo pkg)
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
881 882
    lib_ghc_options    = concatMap (get_ghc_options . libBuildInfo)
                         (allLibraries pkg)
883
    get_ghc_options bi = hcOptions GHC bi ++ hcProfOptions GHC bi
884
                         ++ hcSharedOptions GHC bi
885

Duncan Coutts's avatar
Duncan Coutts committed
886 887
    checkFlags :: [String] -> PackageCheck -> Maybe PackageCheck
    checkFlags flags = check (any (`elem` flags) all_ghc_options)
Duncan Coutts's avatar
Duncan Coutts committed
888 889

    ghcExtension ('-':'f':name) = case name of
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
      "allow-overlapping-instances"    -> enable  OverlappingInstances
      "no-allow-overlapping-instances" -> disable OverlappingInstances
      "th"                             -> enable  TemplateHaskell
      "no-th"                          -> disable TemplateHaskell
      "ffi"                            -> enable  ForeignFunctionInterface
      "no-ffi"                         -> disable ForeignFunctionInterface
      "fi"                             -> enable  ForeignFunctionInterface
      "no-fi"                          -> disable ForeignFunctionInterface
      "monomorphism-restriction"       -> enable  MonomorphismRestriction
      "no-monomorphism-restriction"    -> disable MonomorphismRestriction
      "mono-pat-binds"                 -> enable  MonoPatBinds
      "no-mono-pat-binds"              -> disable MonoPatBinds
      "allow-undecidable-instances"    -> enable  UndecidableInstances
      "no-allow-undecidable-instances" -> disable UndecidableInstances
      "allow-incoherent-instances"     -> enable  IncoherentInstances
      "no-allow-incoherent-instances"  -> disable IncoherentInstances
      "arrows"                         -> enable  Arrows
      "no-arrows"                      -> disable Arrows
      "generics"                       -> enable  Generics
      "no-generics"                    -> disable Generics
      "implicit-prelude"               -> enable  ImplicitPrelude
      "no-implicit-prelude"            -> disable ImplicitPrelude
      "implicit-params"                -> enable  ImplicitParams
      "no-implicit-params"             -> disable ImplicitParams
      "bang-patterns"                  -> enable  BangPatterns
      "no-bang-patterns"               -> disable BangPatterns
      "scoped-type-variables"          -> enable  ScopedTypeVariables
      "no-scoped-type-variables"       -> disable ScopedTypeVariables
      "extended-default-rules"         -> enable  ExtendedDefaultRules
      "no-extended-default-rules"      -> disable ExtendedDefaultRules
920
      _                                -> Nothing
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
921
    ghcExtension "-cpp"             = enable CPP
Duncan Coutts's avatar
Duncan Coutts committed
922 923
    ghcExtension _                  = Nothing

Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
924 925 926
    enable  e = Just (EnableExtension e)
    disable e = Just (DisableExtension e)

Duncan Coutts's avatar
Duncan Coutts committed
927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
checkCCOptions :: PackageDescription -> [PackageCheck]
checkCCOptions pkg =
  catMaybes [

    checkAlternatives "cc-options" "include-dirs"
      [ (flag, dir) | flag@('-':'I':dir) <- all_ccOptions ]

  , checkAlternatives "cc-options" "extra-libraries"
      [ (flag, lib) | flag@('-':'l':lib) <- all_ccOptions ]

  , checkAlternatives "cc-options" "extra-lib-dirs"
      [ (flag, dir) | flag@('-':'L':dir) <- all_ccOptions ]

  , checkAlternatives "ld-options" "extra-libraries"
      [ (flag, lib) | flag@('-':'l':lib) <- all_ldOptions ]

  , checkAlternatives "ld-options" "extra-lib-dirs"
      [ (flag, dir) | flag@('-':'L':dir) <- all_ldOptions ]
945 946 947 948 949 950 951

  , checkCCFlags [ "-O", "-Os", "-O0", "-O1", "-O2", "-O3" ] $
      PackageDistSuspicious $
           "'cc-options: -O[n]' is generally not needed. When building with "
        ++ " optimisations Cabal automatically adds '-O2' for C code. "
        ++ "Setting it yourself interferes with the --disable-optimization "
        ++ "flag."
Duncan Coutts's avatar
Duncan Coutts committed
952 953 954 955 956 957 958
  ]

  where all_ccOptions = [ opts | bi <- allBuildInfo pkg
                              , opts <- ccOptions bi ]
        all_ldOptions = [ opts | bi <- allBuildInfo pkg
                               , opts <- ldOptions bi ]

959 960 961
        checkCCFlags :: [String] -> PackageCheck -> Maybe PackageCheck
        checkCCFlags flags = check (any (`elem` flags) all_ccOptions)

962 963 964 965 966 967 968 969 970
checkCPPOptions :: PackageDescription -> [PackageCheck]
checkCPPOptions pkg =
  catMaybes [
    checkAlternatives "cpp-options" "include-dirs"
      [ (flag, dir) | flag@('-':'I':