Check.hs 97.1 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
import Distribution.Compat.Prelude
37
import Prelude ()
38

39 40 41
import Control.Monad                                 (mapM)
import Data.List                                     (group)
import Distribution.Compat.Lens
42 43
import Distribution.Compiler
import Distribution.License
44 45 46 47 48
import Distribution.Package
import Distribution.PackageDescription
import Distribution.PackageDescription.Configuration
import Distribution.Pretty                           (prettyShow)
import Distribution.Simple.BuildPaths                (autogenPathsModuleName)
John Ericson's avatar
John Ericson committed
49
import Distribution.Simple.BuildToolDepends
50
import Distribution.Simple.CCompiler
51
import Distribution.Simple.Glob
52 53
import Distribution.Simple.Utils                     hiding (findPackageDesc, notice)
import Distribution.System
54
import Distribution.Types.ComponentRequestedSpec
55
import Distribution.Types.CondTree
56
import Distribution.Types.ExeDependency
57
import Distribution.Types.UnqualComponentName
58
import Distribution.Utils.Generic                    (isAscii)
59
import Distribution.Verbosity
60
import Distribution.Version
61
import Language.Haskell.Extension
62 63
import System.FilePath
       (splitDirectories, splitExtension, splitPath, takeExtension, takeFileName, (<.>), (</>))
64

65 66 67 68 69
import qualified Data.ByteString.Lazy      as BS
import qualified Data.Map                  as Map
import qualified Distribution.Compat.DList as DList
import qualified Distribution.SPDX         as SPDX
import qualified System.Directory          as System
70

71 72
import qualified System.Directory        (getDirectoryContents)
import qualified System.FilePath.Windows as FilePath.Windows (isValid)
73

Oleg Grenrus's avatar
Oleg Grenrus committed
74
import qualified Data.Set as Set
75

76
import qualified Distribution.Types.BuildInfo.Lens                 as L
77
import qualified Distribution.Types.GenericPackageDescription.Lens as L
78
import qualified Distribution.Types.PackageDescription.Lens        as L
79

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
-- | 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
99
       -- might be annoying or detrimental when the package is distributed to
100 101 102 103 104
       -- 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 }

105
       -- | Like PackageDistSuspicious but will only display warnings
106
       -- rather than causing abnormal exit when you run 'cabal check'.
107 108
     | PackageDistSuspiciousWarn { explanation :: String }

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

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
123 124
checkSpecVersion :: PackageDescription -> [Int] -> Bool -> PackageCheck
                 -> Maybe PackageCheck
125
checkSpecVersion pkg specver cond pc
126
  | specVersion pkg >= mkVersion specver  = Nothing
127 128
  | otherwise                             = check cond pc

129 130 131 132 133 134
-- ------------------------------------------------------------
-- * Standard checks
-- ------------------------------------------------------------

-- | Check for common mistakes and problems in package descriptions.
--
Ian D. Bollinger's avatar
Ian D. Bollinger committed
135
-- This is the standard collection of checks covering all aspects except
136 137 138
-- for checks that require looking at files within the package. For those
-- see 'checkPackageFiles'.
--
139 140 141 142 143 144 145 146 147
-- 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
148
  ++ checkConditionals gpkg
149
  ++ checkPackageVersions gpkg
150
  ++ checkDevelopmentOnlyFlags gpkg
Oleg Grenrus's avatar
Oleg Grenrus committed
151
  ++ checkFlagNames gpkg
Oleg Grenrus's avatar
Oleg Grenrus committed
152
  ++ checkUnusedFlags gpkg
Oleg Grenrus's avatar
Oleg Grenrus committed
153
  ++ checkUnicodeXFields gpkg
154
  ++ checkPathsModuleExtensions pkg
155 156 157 158
  where
    pkg = fromMaybe (flattenPackageDescription gpkg) mpkg

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


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

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

184
    check (null . unPackageName . packageName $ pkg) $
185 186
      PackageBuildImpossible "No 'name' field."

187
  , check (nullVersion == packageVersion pkg) $
188 189
      PackageBuildImpossible "No 'version' field."

190 191 192
  , check (all ($ pkg) [ null . executables
                       , null . testSuites
                       , null . benchmarks
193 194
                       , null . allLibraries
                       , null . foreignLibs ]) $
195
      PackageBuildImpossible
196
        "No executables, libraries, tests, or benchmarks found. Nothing to do."
197

198 199 200 201
  , 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
202
  , check (not (null duplicateNames)) $
203 204
      PackageBuildImpossible $ "Duplicate sections: "
        ++ commaSep (map unUnqualComponentName duplicateNames)
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
205 206
        ++ ". The name of every library, executable, test suite,"
        ++ " and benchmark section in"
tibbe's avatar
tibbe committed
207
        ++ " the package must be unique."
208 209

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

222
  ++ concatMap (checkLibrary    pkg) (allLibraries pkg)
223 224 225
  ++ concatMap (checkExecutable pkg) (executables pkg)
  ++ concatMap (checkTestSuite  pkg) (testSuites pkg)
  ++ concatMap (checkBenchmark  pkg) (benchmarks pkg)
226 227 228

  ++ catMaybes [

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

245
checkLibrary :: PackageDescription -> Library -> [PackageCheck]
246
checkLibrary pkg lib =
247 248
  catMaybes [

249
    check (not (null moduleDuplicates)) $
250
       PackageBuildImpossible $
251
            "Duplicate modules in library: "
252
         ++ commaSep (map prettyShow moduleDuplicates)
253

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

262 263
    -- check use of signatures sections
  , checkVersion [1,25] (not (null (signatures lib))) $
264
      PackageDistInexcusable $
265
           "To use the 'signatures' field the package needs to specify "
266
        ++ "at least 'cabal-version: 2.0'."
fmaste's avatar
fmaste committed
267 268 269

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

275 276
  ]

277
  where
278 279
    checkVersion :: [Int] -> Bool -> PackageCheck -> Maybe PackageCheck
    checkVersion ver cond pc
280
      | specVersion pkg >= mkVersion ver       = Nothing
281 282
      | otherwise                              = check cond pc

283 284
    -- TODO: not sure if this check is always right in Backpack
    moduleDuplicates = dups (explicitLibModules lib ++
285
                             map moduleReexportName (reexportedModules lib))
Duncan Coutts's avatar
Duncan Coutts committed
286

287 288
checkExecutable :: PackageDescription -> Executable -> [PackageCheck]
checkExecutable pkg exe =
289 290 291 292
  catMaybes [

    check (null (modulePath exe)) $
      PackageBuildImpossible $
293
        "No 'main-is' field found for executable " ++ prettyShow (exeName exe)
294 295

  , check (not (null (modulePath exe))
296
       && (not $ fileExtensionSupportedLanguage $ modulePath exe)) $
297
      PackageBuildImpossible $
298 299 300
           "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
301

302 303 304 305 306 307 308
  , 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
309
  , check (not (null moduleDuplicates)) $
310
       PackageBuildImpossible $
311 312
            "Duplicate modules in executable '" ++ prettyShow (exeName exe) ++ "': "
         ++ commaSep (map prettyShow moduleDuplicates)
fmaste's avatar
fmaste committed
313 314 315 316 317

    -- check that all autogen-modules appear on other-modules
  , check
      (not $ and $ map (flip elem (exeModules exe)) (exeModulesAutogen exe)) $
      PackageBuildImpossible $
318
           "On executable '" ++ prettyShow (exeName exe) ++ "' an 'autogen-module' is not "
fmaste's avatar
fmaste committed
319
        ++ "on 'other-modules'"
320
  ]
321 322
  where
    moduleDuplicates = dups (exeModules exe)
323

324 325
checkTestSuite :: PackageDescription -> TestSuite -> [PackageCheck]
checkTestSuite pkg test =
326 327
  catMaybes [

328 329 330
    case testInterface test of
      TestSuiteUnsupported tt@(TestTypeUnknown _ _) -> Just $
        PackageBuildWarning $
331
             quote (prettyShow tt) ++ " is not a known type of test suite. "
332
          ++ "The known test suite types are: "
333
          ++ commaSep (map prettyShow knownTestTypes)
334 335 336

      TestSuiteUnsupported tt -> Just $
        PackageBuildWarning $
337
             quote (prettyShow tt) ++ " is not a supported test suite version. "
338
          ++ "The known test suite types are: "
339
          ++ commaSep (map prettyShow knownTestTypes)
340 341 342
      _ -> Nothing

  , check (not $ null moduleDuplicates) $
343
      PackageBuildImpossible $
344 345
           "Duplicate modules in test suite '" ++ prettyShow (testName test) ++ "': "
        ++ commaSep (map prettyShow moduleDuplicates)
346

347 348 349
  , check mainIsWrongExt $
      PackageBuildImpossible $
           "The 'main-is' field must specify a '.hs' or '.lhs' file "
350 351
        ++ "(even if it is generated by a preprocessor), "
        ++ "or it may specify a C/C++/obj-C source file."
352

353 354 355 356
  , 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
357 358 359 360 361 362 363 364

    -- check that all autogen-modules appear on other-modules
  , check
      (not $ and $ map
        (flip elem (testModules test))
        (testModulesAutogen test)
      ) $
      PackageBuildImpossible $
365
           "On test suite '" ++ prettyShow (testName test) ++ "' an 'autogen-module' is not "
fmaste's avatar
fmaste committed
366
        ++ "on 'other-modules'"
367
  ]
368 369
  where
    moduleDuplicates = dups $ testModules test
370 371

    mainIsWrongExt = case testInterface test of
372
      TestSuiteExeV10 _ f -> not $ fileExtensionSupportedLanguage f
373 374
      _                   -> False

375 376 377 378
    mainIsNotHsExt = case testInterface test of
      TestSuiteExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]
      _                   -> False

tibbe's avatar
tibbe committed
379
checkBenchmark :: PackageDescription -> Benchmark -> [PackageCheck]
380
checkBenchmark _pkg bm =
tibbe's avatar
tibbe committed
381 382 383 384 385
  catMaybes [

    case benchmarkInterface bm of
      BenchmarkUnsupported tt@(BenchmarkTypeUnknown _ _) -> Just $
        PackageBuildWarning $
386
             quote (prettyShow tt) ++ " is not a known type of benchmark. "
tibbe's avatar
tibbe committed
387
          ++ "The known benchmark types are: "
388
          ++ commaSep (map prettyShow knownBenchmarkTypes)
tibbe's avatar
tibbe committed
389 390 391

      BenchmarkUnsupported tt -> Just $
        PackageBuildWarning $
392
             quote (prettyShow tt) ++ " is not a supported benchmark version. "
tibbe's avatar
tibbe committed
393
          ++ "The known benchmark types are: "
394
          ++ commaSep (map prettyShow knownBenchmarkTypes)
tibbe's avatar
tibbe committed
395 396 397
      _ -> Nothing

  , check (not $ null moduleDuplicates) $
398
      PackageBuildImpossible $
399 400
           "Duplicate modules in benchmark '" ++ prettyShow (benchmarkName bm) ++ "': "
        ++ commaSep (map prettyShow moduleDuplicates)
tibbe's avatar
tibbe committed
401 402 403 404 405

  , 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
406 407 408 409 410 411 412 413

    -- check that all autogen-modules appear on other-modules
  , check
      (not $ and $ map
        (flip elem (benchmarkModules bm))
        (benchmarkModulesAutogen bm)
      ) $
      PackageBuildImpossible $
414
             "On benchmark '" ++ prettyShow (benchmarkName bm) ++ "' an 'autogen-module' is "
fmaste's avatar
fmaste committed
415
          ++ "not on 'other-modules'"
tibbe's avatar
tibbe committed
416 417 418 419 420 421 422 423
  ]
  where
    moduleDuplicates = dups $ benchmarkModules bm

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

424 425 426 427 428 429 430 431
-- ------------------------------------------------------------
-- * Additional pure checks
-- ------------------------------------------------------------

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

432
    check (not . FilePath.Windows.isValid . prettyShow . packageName $ pkg) $
433
      PackageDistInexcusable $
434
           "Unfortunately, the package name '" ++ prettyShow (packageName pkg)
435 436 437 438
        ++ "' 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."

439
  , check ((isPrefixOf "z-") . prettyShow . packageName $ pkg) $
440 441 442 443
      PackageDistInexcusable $
           "Package names with the prefix 'z-' are reserved by Cabal and "
        ++ "cannot be used."

444
  , check (isNothing (buildTypeRaw pkg) && specVersion pkg < mkVersion [2,1]) $
445 446 447 448
      PackageBuildWarning $
           "No 'build-type' specified. If you do not need a custom Setup.hs or "
        ++ "./configure script then use 'build-type: Simple'."

449
  , check (isJust (setupBuildInfo pkg) && buildType pkg /= Custom) $
450 451 452 453 454
      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."

455 456
  , check (not (null unknownCompilers)) $
      PackageBuildWarning $
457
        "Unknown compiler " ++ commaSep (map quote unknownCompilers)
458 459
                            ++ " in 'tested-with' field."

460 461 462 463
  , check (not (null unknownLanguages)) $
      PackageBuildWarning $
        "Unknown languages: " ++ commaSep unknownLanguages

464 465
  , check (not (null unknownExtensions)) $
      PackageBuildWarning $
466
        "Unknown extensions: " ++ commaSep unknownExtensions
467

468 469 470 471 472 473 474
  , 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."

475
  , check (not (null ourDeprecatedExtensions)) $
476 477
      PackageDistSuspicious $
           "Deprecated extensions: "
478
        ++ commaSep (map (quote . prettyShow . fst) ourDeprecatedExtensions)
EyalLotem's avatar
EyalLotem committed
479
        ++ ". " ++ unwords
480 481
             [ "Instead of '" ++ prettyShow ext
            ++ "' use '" ++ prettyShow replacement ++ "'."
482
             | (ext, Just replacement) <- ourDeprecatedExtensions ]
483

484 485 486 487 488 489
  , check (null (category pkg)) $
      PackageDistSuspicious "No 'category' field."

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

490
  , check (null (synopsis pkg) && null (description pkg)) $
EyalLotem's avatar
EyalLotem committed
491
      PackageDistInexcusable "No 'synopsis' or 'description' field."
492 493 494 495 496

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

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

Ian D. Bollinger's avatar
Ian D. Bollinger committed
499
    --TODO: recommend the bug reports URL, author and homepage fields
500 501 502
    --TODO: recommend not using the stability field
    --TODO: recommend specifying a source repo

503 504 505
  , check (length (synopsis pkg) >= 80) $
      PackageDistSuspicious
        "The 'synopsis' field is rather long (max 80 chars is recommended)."
506

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
    -- 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."

523 524 525 526
    -- check use of impossible constraints "tested-with: GHC== 6.10 && ==6.12"
  , check (not (null testedWithImpossibleRanges)) $
      PackageDistInexcusable $
           "Invalid 'tested-with' version range: "
527
        ++ commaSep (map prettyShow testedWithImpossibleRanges)
528 529 530 531
        ++ ". 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'."
532

John Ericson's avatar
John Ericson committed
533
  , check (not (null depInternalLibraryWithExtraVersion)) $
534
      PackageBuildWarning $
John Ericson's avatar
John Ericson committed
535
           "The package has an extraneous version range for a dependency on an "
536
        ++ "internal library: "
537
        ++ commaSep (map prettyShow depInternalLibraryWithExtraVersion)
John Ericson's avatar
John Ericson committed
538 539 540 541 542 543 544
        ++ ". 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: "
545
        ++ commaSep (map prettyShow depInternalLibraryWithImpossibleVersion)
John Ericson's avatar
John Ericson committed
546 547 548 549 550 551 552
        ++ ". 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: "
553
        ++ commaSep (map prettyShow depInternalExecutableWithExtraVersion)
John Ericson's avatar
John Ericson committed
554 555 556 557 558 559 560
        ++ ". 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: "
561
        ++ commaSep (map prettyShow depInternalExecutableWithImpossibleVersion)
John Ericson's avatar
John Ericson committed
562 563 564 565 566 567
        ++ ". 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: "
568
        ++ commaSep (map prettyShow depInternalExecutableWithImpossibleVersion)
569
  ]
570 571
  where
    unknownCompilers  = [ name | (OtherCompiler name, _) <- testedWith pkg ]
572 573
    unknownLanguages  = [ name | bi <- allBuildInfo pkg
                               , UnknownLanguage name <- allLanguages bi ]
574
    unknownExtensions = [ name | bi <- allBuildInfo pkg
575
                               , UnknownExtension name <- allExtensions bi
576
                               , name `notElem` map prettyShow knownLanguages ]
577 578
    ourDeprecatedExtensions = nub $ catMaybes
      [ find ((==ext) . fst) deprecatedExtensions
579
      | bi <- allBuildInfo pkg
Ian Lynagh's avatar
Ian Lynagh committed
580
      , ext <- allExtensions bi ]
581 582 583
    languagesUsedAsExtensions =
      [ name | bi <- allBuildInfo pkg
             , UnknownExtension name <- allExtensions bi
584
             , name `elem` map prettyShow knownLanguages ]
585

586
    testedWithImpossibleRanges =
587
      [ Dependency (mkPackageName (prettyShow compiler)) vr Set.empty
588 589 590
      | (compiler, vr) <- testedWith pkg
      , isNoVersion vr ]

591
    internalLibraries =
592
        map (maybe (packageName pkg) (unqualComponentNameToPackageName) . libName)
593
            (allLibraries pkg)
John Ericson's avatar
John Ericson committed
594 595 596 597

    internalExecutables = map exeName $ executables pkg

    internalLibDeps =
598 599
      [ dep
      | bi <- allBuildInfo pkg
600
      , dep@(Dependency name _ _) <- targetBuildDepends bi
601 602 603
      , name `elem` internalLibraries
      ]

John Ericson's avatar
John Ericson committed
604 605 606
    internalExeDeps =
      [ dep
      | bi <- allBuildInfo pkg
607 608
      , dep <- getAllToolDependencies pkg bi
      , isInternal pkg dep
John Ericson's avatar
John Ericson committed
609 610 611 612
      ]

    depInternalLibraryWithExtraVersion =
      [ dep
613
      | dep@(Dependency _ versionRange _) <- internalLibDeps
John Ericson's avatar
John Ericson committed
614 615 616 617 618 619
      , not $ isAnyVersion versionRange
      , packageVersion pkg `withinRange` versionRange
      ]

    depInternalLibraryWithImpossibleVersion =
      [ dep
620
      | dep@(Dependency _ versionRange _) <- internalLibDeps
John Ericson's avatar
John Ericson committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
      , 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
      ]

643

644
checkLicense :: PackageDescription -> [PackageCheck]
645 646 647 648 649 650 651 652 653 654
checkLicense pkg = case licenseRaw pkg of
    Right l -> checkOldLicense pkg l
    Left  l -> checkNewLicense pkg l

checkNewLicense :: PackageDescription -> SPDX.License -> [PackageCheck]
checkNewLicense _pkg lic = catMaybes
    [ check (lic == SPDX.NONE) $
        PackageDistInexcusable
            "The 'license' field is missing or is NONE."
    ]
655

656 657 658
checkOldLicense :: PackageDescription -> License -> [PackageCheck]
checkOldLicense pkg lic = catMaybes
  [ check (lic == UnspecifiedLicense) $
659
      PackageDistInexcusable
660
        "The 'license' field is missing."
661

662
  , check (lic == AllRightsReserved) $
663 664
      PackageDistSuspicious
        "The 'license' is AllRightsReserved. Is that really what you want?"
665 666 667 668 669 670 671 672 673

  , checkVersion [1,4] (lic `notElem` compatLicenses) $
      PackageDistInexcusable $
           "Unfortunately the license " ++ quote (prettyShow (license pkg))
        ++ " messes up the parser in earlier Cabal versions so you need to "
        ++ "specify 'cabal-version: >= 1.4'. Alternatively if you require "
        ++ "compatibility with earlier Cabal versions then use 'OtherLicense'."

  , case lic of
674 675
      UnknownLicense l -> Just $
        PackageBuildWarning $
676 677
             quote ("license: " ++ l) ++ " is not a recognised license. The "
          ++ "known licenses are: "
678
          ++ commaSep (map prettyShow knownLicenses)
679 680
      _ -> Nothing

681
  , check (lic == BSD4) $
682 683
      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
  , case unknownLicenseVersion (lic) of
688 689
      Just knownVersions -> Just $
        PackageDistSuspicious $
690
             "'license: " ++ prettyShow (lic) ++ "' is not a known "
691
          ++ "version of that license. The known versions are "
692
          ++ commaSep (map prettyShow knownVersions)
693 694 695 696
          ++ ". If this is not a mistake and you think it should be a known "
          ++ "version then please file a ticket."
      _ -> Nothing

697
  , check (lic `notElem` [ AllRightsReserved
698
                                 , 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
    checkVersion :: [Int] -> Bool -> PackageCheck -> Maybe PackageCheck
    checkVersion ver cond pc
      | specVersion pkg >= mkVersion ver       = Nothing
      | otherwise                              = check cond pc

    compatLicenses = [ GPL Nothing, LGPL Nothing, AGPL Nothing, BSD3, BSD4
                     , PublicDomain, AllRightsReserved
                     , UnspecifiedLicense, OtherLicense ]

728 729 730 731 732 733 734 735 736 737
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
738
  , check (isNothing (repoType repo)) $
739 740 741
      PackageDistInexcusable
        "The source-repository 'type' is a required field."

EyalLotem's avatar
EyalLotem committed
742
  , check (isNothing (repoLocation repo)) $
743 744 745
      PackageDistInexcusable
        "The source-repository 'location' is a required field."

EyalLotem's avatar
EyalLotem committed
746
  , check (repoType repo == Just CVS && isNothing (repoModule repo)) $
747 748 749
      PackageDistInexcusable
        "For a CVS source-repository, the 'module' is a required field."

EyalLotem's avatar
EyalLotem committed
750
  , check (repoKind repo == RepoThis && isNothing (repoTag repo)) $
751 752 753 754 755
      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."

756
  , check (maybe False isAbsoluteOnAnyPlatform (repoSubdir repo)) $
757 758 759 760 761 762 763
      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.

764 765 766 767 768 769 770 771 772 773 774 775 776
-- | Checks GHC options from all ghc-*-options fields in the given
-- PackageDescription and reports commonly misused or non-portable flags
checkAllGhcOptions :: PackageDescription -> [PackageCheck]
checkAllGhcOptions pkg =
    checkGhcOptions "ghc-options" (hcOptions GHC) pkg
 ++ checkGhcOptions "ghc-prof-options" (hcProfOptions GHC) pkg
 ++ checkGhcOptions "ghc-shared-options" (hcSharedOptions GHC) pkg

-- | Extracts GHC options belonging to the given field from the given
-- PackageDescription using given function and checks them for commonly misused
-- or non-portable flags
checkGhcOptions :: String -> (BuildInfo -> [String]) -> PackageDescription -> [PackageCheck]
checkGhcOptions fieldName getOptions pkg =
777 778
  catMaybes [

779
    checkFlags ["-fasm"] $
780
      PackageDistInexcusable $
781
           "'" ++ fieldName ++ ": -fasm' is unnecessary and will not work on CPU "
782
        ++ "architectures other than x86, x86-64, ppc or sparc."
783

Duncan Coutts's avatar
Duncan Coutts committed
784 785
  , checkFlags ["-fvia-C"] $
      PackageDistSuspicious $
786
           "'" ++ fieldName ++": -fvia-C' is usually unnecessary. If your package "
787 788 789
        ++ "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
790 791 792

  , checkFlags ["-fhpc"] $
      PackageDistInexcusable $
793
           "'" ++ fieldName ++ ": -fhpc' is not not necessary. Use the configure flag "
794
        ++ " --enable-coverage instead."
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
795

Duncan Coutts's avatar
Duncan Coutts committed
796
  , checkFlags ["-prof"] $
797
      PackageBuildWarning $
798
           "'" ++ fieldName ++ ": -prof' is not necessary and will lead to problems "
799
        ++ "when used on a library. Use the configure flag "
gershomb's avatar
gershomb committed
800
        ++ "--enable-library-profiling and/or --enable-profiling."
Duncan Coutts's avatar
Duncan Coutts committed
801 802

  , checkFlags ["-o"] $
803
      PackageBuildWarning $
804
           "'" ++ fieldName ++ ": -o' is not needed. "
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
805
        ++ "The output files are named automatically."
Duncan Coutts's avatar
Duncan Coutts committed
806 807

  , checkFlags ["-hide-package"] $
808
      PackageBuildWarning $
809
      "'" ++ fieldName ++ ": -hide-package' is never needed. "
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
810
      ++ "Cabal hides all packages."
Duncan Coutts's avatar
Duncan Coutts committed
811

Duncan Coutts's avatar
Duncan Coutts committed
812
  , checkFlags ["--make"] $
813
      PackageBuildWarning $
814
      "'" ++ fieldName ++ ": --make' is never needed. Cabal uses this automatically."
Duncan Coutts's avatar
Duncan Coutts committed
815

Duncan Coutts's avatar
Duncan Coutts committed
816 817
  , checkFlags ["-main-is"] $
      PackageDistSuspicious $
818
      "'" ++ fieldName ++ ": -main-is' is not portable."
Duncan Coutts's avatar
Duncan Coutts committed
819

820
  , checkNonTestAndBenchmarkFlags ["-O0", "-Onot"] $
821
      PackageDistSuspicious $
822
      "'" ++ fieldName ++ ": -O0' is not needed. "
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
823
      ++ "Use the --disable-optimization configure flag."
Duncan Coutts's avatar
Duncan Coutts committed
824

825 826
  , checkTestAndBenchmarkFlags ["-O0", "-Onot"] $
      PackageDistSuspiciousWarn $
827
      "'" ++ fieldName ++ ": -O0' is not needed. "
828 829
      ++ "Use the --disable-optimization configure flag."

Duncan Coutts's avatar
Duncan Coutts committed
830
  , checkFlags [ "-O", "-O1"] $
831
      PackageDistInexcusable $
832
      "'" ++ fieldName ++ ": -O' is not needed. "
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
833 834
      ++ "Cabal automatically adds the '-O' flag. "
      ++ "Setting it yourself interferes with the --disable-optimization flag."
835

Duncan Coutts's avatar
Duncan Coutts committed
836
  , checkFlags ["-O2"] $
837
      PackageDistSuspiciousWarn $
838
      "'" ++ fieldName ++ ": -O2' is rarely needed. "
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
839 840
      ++ "Check that it is giving a real benefit "
      ++ "and not just imposing longer compile times on your users."
841

Ben Gamari's avatar
Ben Gamari committed
842 843
  , checkFlags ["-split-sections"] $
      PackageBuildWarning $
844
        "'" ++ fieldName ++ ": -split-sections' is not needed. "
Ben Gamari's avatar
Ben Gamari committed
845 846
        ++ "Use the --enable-split-sections configure flag."

Duncan Coutts's avatar
Duncan Coutts committed
847
  , checkFlags ["-split-objs"] $
848
      PackageBuildWarning $
849
        "'" ++ fieldName ++ ": -split-objs' is not needed. "
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
850
        ++ "Use the --enable-split-objs configure flag."
Duncan Coutts's avatar
Duncan Coutts committed
851

852
  , checkFlags ["-optl-Wl,-s", "-optl-s"] $
853
      PackageDistInexcusable $
854
           "'" ++ fieldName ++ ": -optl-Wl,-s' is not needed and is not portable to all"