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

39
import Distribution.PackageDescription
40
import Distribution.PackageDescription.Configuration
41
42
43
import Distribution.Compiler
import Distribution.System
import Distribution.License
fmaste's avatar
fmaste committed
44
import Distribution.Simple.BuildPaths (autogenPathsModuleName)
45
import Distribution.Simple.CCompiler
46
import Distribution.Types.ComponentRequestedSpec
47
48
import Distribution.Types.Dependency
import Distribution.Types.UnqualComponentName
49
import Distribution.Simple.Utils hiding (findPackageDesc, notice)
50
import Distribution.Version
51
import Distribution.Package
52
import Distribution.Text
53
54
import Language.Haskell.Extension

55
import Control.Monad (mapM)
56
import Data.List  (group)
57
58
59
60
import qualified System.Directory as System
         ( doesFileExist, doesDirectoryExist )
import qualified Data.Map as Map

61
import qualified Text.PrettyPrint as Disp
62
import Text.PrettyPrint ((<+>))
63

Oleg Grenrus's avatar
Oleg Grenrus committed
64
65
import qualified System.Directory (getDirectoryContents)
import System.IO (openBinaryFile, IOMode(ReadMode), hGetContents)
66
import System.FilePath
67
         ( (</>), takeExtension, splitDirectories, splitPath, splitExtension )
68
69
import System.FilePath.Windows as FilePath.Windows
         ( isValid )
70

71

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
-- | 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
91
       -- might be annoying or detrimental when the package is distributed to
92
93
94
95
96
       -- 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 }

97
       -- | Like PackageDistSuspicious but will only display warnings
98
       -- rather than causing abnormal exit when you run 'cabal check'.
99
100
     | PackageDistSuspiciousWarn { explanation :: String }

Ian D. Bollinger's avatar
Ian D. Bollinger committed
101
       -- | An issue that is OK in the author's environment but is almost
102
103
104
105
       -- certain to be a portability problem for other environments. We can
       -- quite legitimately refuse to publicly distribute packages with these
       -- problems.
     | PackageDistInexcusable { explanation :: String }
106
  deriving (Eq)
107
108
109
110
111
112
113
114

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
115
116
checkSpecVersion :: PackageDescription -> [Int] -> Bool -> PackageCheck
                 -> Maybe PackageCheck
117
checkSpecVersion pkg specver cond pc
118
  | specVersion pkg >= mkVersion specver  = Nothing
119
120
  | otherwise                             = check cond pc

121
122
123
124
125
126
-- ------------------------------------------------------------
-- * Standard checks
-- ------------------------------------------------------------

-- | Check for common mistakes and problems in package descriptions.
--
Ian D. Bollinger's avatar
Ian D. Bollinger committed
127
-- This is the standard collection of checks covering all aspects except
128
129
130
-- for checks that require looking at files within the package. For those
-- see 'checkPackageFiles'.
--
131
132
133
134
135
136
137
138
139
-- 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
140
  ++ checkConditionals gpkg
141
  ++ checkPackageVersions gpkg
142
  ++ checkDevelopmentOnlyFlags gpkg
143
144
145
146
  where
    pkg = fromMaybe (flattenPackageDescription gpkg) mpkg

--TODO: make this variant go away
refold's avatar
Typo.    
refold committed
147
--      we should always know the GenericPackageDescription
148
149
checkConfiguredPackage :: PackageDescription -> [PackageCheck]
checkConfiguredPackage pkg =
150
151
152
    checkSanity pkg
 ++ checkFields pkg
 ++ checkLicense pkg
153
 ++ checkSourceRepos pkg
154
 ++ checkGhcOptions pkg
Duncan Coutts's avatar
Duncan Coutts committed
155
 ++ checkCCOptions pkg
156
 ++ checkCPPOptions pkg
157
 ++ checkPaths pkg
158
 ++ checkCabalVersion pkg
159
160
161
162
163
164
165
166
167
168
169
170


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

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

171
    check (null . unPackageName . packageName $ pkg) $
172
173
      PackageBuildImpossible "No 'name' field."

174
  , check (nullVersion == packageVersion pkg) $
175
176
      PackageBuildImpossible "No 'version' field."

177
178
179
  , check (all ($ pkg) [ null . executables
                       , null . testSuites
                       , null . benchmarks
180
181
                       , null . allLibraries
                       , null . foreignLibs ]) $
182
      PackageBuildImpossible
183
        "No executables, libraries, tests, or benchmarks found. Nothing to do."
184

185
186
187
188
  , 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
189
  , check (not (null duplicateNames)) $
190
191
      PackageBuildImpossible $ "Duplicate sections: "
        ++ commaSep (map unUnqualComponentName duplicateNames)
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
192
193
        ++ ". The name of every library, executable, test suite,"
        ++ " and benchmark section in"
tibbe's avatar
tibbe committed
194
        ++ " the package must be unique."
195
196

  -- NB: but it's OK for executables to have the same name!
197
198
  -- TODO shouldn't need to compare on the string level
  , check (any (== display (packageName pkg)) (display <$> subLibNames)) $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
199
200
201
202
203
      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"
204
        ++ " from 'library: '" ++ display (packageName pkg) ++ "' to 'library'."
205
  ]
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
206
207
  --TODO: check for name clashes case insensitively: windows file systems cannot
  --cope.
208

209
  ++ concatMap (checkLibrary    pkg) (allLibraries pkg)
210
211
212
  ++ concatMap (checkExecutable pkg) (executables pkg)
  ++ concatMap (checkTestSuite  pkg) (testSuites pkg)
  ++ concatMap (checkBenchmark  pkg) (benchmarks pkg)
213
214
215

  ++ catMaybes [

216
    check (specVersion pkg > cabalVersion) $
217
      PackageBuildImpossible $
218
219
220
           "This package description follows version "
        ++ display (specVersion pkg) ++ " of the Cabal specification. This "
        ++ "tool only supports up to version " ++ display cabalVersion ++ "."
221
  ]
222
  where
223
    -- The public 'library' gets special dispensation, because it
224
    -- is common practice to export a library and name the executable
225
226
    -- the same as the package.
    subLibNames = catMaybes . map libName $ subLibraries pkg
227
    exeNames = map exeName $ executables pkg
228
    testNames = map testName $ testSuites pkg
tibbe's avatar
tibbe committed
229
    bmNames = map benchmarkName $ benchmarks pkg
230
    duplicateNames = dups $ subLibNames ++ exeNames ++ testNames ++ bmNames
231

232
checkLibrary :: PackageDescription -> Library -> [PackageCheck]
233
checkLibrary pkg lib =
234
235
  catMaybes [

236
    check (not (null moduleDuplicates)) $
237
       PackageBuildImpossible $
238
239
            "Duplicate modules in library: "
         ++ commaSep (map display moduleDuplicates)
240

241
242
  -- TODO: This check is bogus if a required-signature was passed through
  , check (null (explicitLibModules lib) && null (reexportedModules lib)) $
243
      PackageDistSuspiciousWarn $
244
245
           "Library " ++ (case libName lib of
                            Nothing -> ""
246
                            Just n -> display n
247
                            ) ++ "does not expose any modules"
248

249
250
    -- check use of signatures sections
  , checkVersion [1,25] (not (null (signatures lib))) $
251
      PackageDistInexcusable $
252
253
           "To use the 'signatures' field the package needs to specify "
        ++ "at least 'cabal-version: >= 1.25'."
fmaste's avatar
fmaste committed
254
255
256

    -- check that all autogen-modules appear on other-modules or exposed-modules
  , check
257
      (not $ and $ map (flip elem (explicitLibModules lib)) (libModulesAutogen lib)) $
fmaste's avatar
fmaste committed
258
259
260
261
      PackageBuildImpossible $
           "An 'autogen-module' is neither on 'exposed-modules' or "
        ++ "'other-modules'."

262
263
  ]

264
  where
265
266
    checkVersion :: [Int] -> Bool -> PackageCheck -> Maybe PackageCheck
    checkVersion ver cond pc
267
      | specVersion pkg >= mkVersion ver       = Nothing
268
269
      | otherwise                              = check cond pc

270
271
    -- TODO: not sure if this check is always right in Backpack
    moduleDuplicates = dups (explicitLibModules lib ++
272
                             map moduleReexportName (reexportedModules lib))
Duncan Coutts's avatar
Duncan Coutts committed
273

274
275
checkExecutable :: PackageDescription -> Executable -> [PackageCheck]
checkExecutable pkg exe =
276
277
278
279
  catMaybes [

    check (null (modulePath exe)) $
      PackageBuildImpossible $
280
        "No 'main-is' field found for executable " ++ display (exeName exe)
281
282

  , check (not (null (modulePath exe))
283
       && (not $ fileExtensionSupportedLanguage $ modulePath exe)) $
284
      PackageBuildImpossible $
285
286
287
           "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
288

289
290
291
292
293
294
295
  , 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
296
  , check (not (null moduleDuplicates)) $
297
       PackageBuildImpossible $
298
            "Duplicate modules in executable '" ++ display (exeName exe) ++ "': "
299
         ++ commaSep (map display moduleDuplicates)
fmaste's avatar
fmaste committed
300
301
302
303
304

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

308
  ]
309
310
  where
    moduleDuplicates = dups (exeModules exe)
311

312
313
checkTestSuite :: PackageDescription -> TestSuite -> [PackageCheck]
checkTestSuite pkg test =
314
315
  catMaybes [

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
    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) $
331
      PackageBuildImpossible $
332
           "Duplicate modules in test suite '" ++ display (testName test) ++ "': "
333
        ++ commaSep (map display moduleDuplicates)
334

335
336
337
  , check mainIsWrongExt $
      PackageBuildImpossible $
           "The 'main-is' field must specify a '.hs' or '.lhs' file "
338
339
        ++ "(even if it is generated by a preprocessor), "
        ++ "or it may specify a C/C++/obj-C source file."
340

341
342
343
344
  , 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
345
346
347
348
349
350
351
352

    -- check that all autogen-modules appear on other-modules
  , check
      (not $ and $ map
        (flip elem (testModules test))
        (testModulesAutogen test)
      ) $
      PackageBuildImpossible $
353
           "On test suite '" ++ display (testName test) ++ "' an 'autogen-module' is not "
fmaste's avatar
fmaste committed
354
        ++ "on 'other-modules'"
355
  ]
356
357
  where
    moduleDuplicates = dups $ testModules test
358
359

    mainIsWrongExt = case testInterface test of
360
      TestSuiteExeV10 _ f -> not $ fileExtensionSupportedLanguage f
361
362
      _                   -> False

363
364
365
366
    mainIsNotHsExt = case testInterface test of
      TestSuiteExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]
      _                   -> False

tibbe's avatar
tibbe committed
367
checkBenchmark :: PackageDescription -> Benchmark -> [PackageCheck]
368
checkBenchmark _pkg bm =
tibbe's avatar
tibbe committed
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
  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) $
386
      PackageBuildImpossible $
387
           "Duplicate modules in benchmark '" ++ display (benchmarkName bm) ++ "': "
tibbe's avatar
tibbe committed
388
389
390
391
392
393
        ++ 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
394
395
396
397
398
399
400
401

    -- check that all autogen-modules appear on other-modules
  , check
      (not $ and $ map
        (flip elem (benchmarkModules bm))
        (benchmarkModulesAutogen bm)
      ) $
      PackageBuildImpossible $
402
             "On benchmark '" ++ display (benchmarkName bm) ++ "' an 'autogen-module' is "
fmaste's avatar
fmaste committed
403
          ++ "not on 'other-modules'"
tibbe's avatar
tibbe committed
404
405
406
407
408
409
410
411
  ]
  where
    moduleDuplicates = dups $ benchmarkModules bm

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

412
413
414
415
416
417
418
419
-- ------------------------------------------------------------
-- * Additional pure checks
-- ------------------------------------------------------------

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

420
421
422
423
424
425
426
    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."

427
428
429
430
431
  , check ((isPrefixOf "z-") . display . packageName $ pkg) $
      PackageDistInexcusable $
           "Package names with the prefix 'z-' are reserved by Cabal and "
        ++ "cannot be used."

432
  , check (isNothing (buildType pkg)) $
433
434
435
436
437
438
439
440
441
      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: "
442
          ++ commaSep (map display knownBuildTypes)
443
444
      _ -> Nothing

445
446
447
448
449
450
  , 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."

451
452
  , check (not (null unknownCompilers)) $
      PackageBuildWarning $
453
        "Unknown compiler " ++ commaSep (map quote unknownCompilers)
454
455
                            ++ " in 'tested-with' field."

456
457
458
459
  , check (not (null unknownLanguages)) $
      PackageBuildWarning $
        "Unknown languages: " ++ commaSep unknownLanguages

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

464
465
466
467
468
469
470
  , 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."

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

480
481
482
483
484
485
  , check (null (category pkg)) $
      PackageDistSuspicious "No 'category' field."

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

486
  , check (null (synopsis pkg) && null (description pkg)) $
EyalLotem's avatar
EyalLotem committed
487
      PackageDistInexcusable "No 'synopsis' or 'description' field."
488
489
490
491
492

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

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

Ian D. Bollinger's avatar
Ian D. Bollinger committed
495
    --TODO: recommend the bug reports URL, author and homepage fields
496
497
498
    --TODO: recommend not using the stability field
    --TODO: recommend specifying a source repo

499
500
501
  , check (length (synopsis pkg) >= 80) $
      PackageDistSuspicious
        "The 'synopsis' field is rather long (max 80 chars is recommended)."
502

503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
    -- 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."

519
520
521
522
523
524
525
526
527
    -- 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'."
528
529
530
531
532
533
534
535

  , check (not (null buildDependsRangeOnInternalLibrary)) $
      PackageBuildWarning $
           "The package has a version range for a dependency on an "
        ++ "internal library: "
        ++ commaSep (map display buildDependsRangeOnInternalLibrary)
        ++ ". This version range has no semantic meaning and can be "
        ++ "removed."
536
  ]
537
538
  where
    unknownCompilers  = [ name | (OtherCompiler name, _) <- testedWith pkg ]
539
540
    unknownLanguages  = [ name | bi <- allBuildInfo pkg
                               , UnknownLanguage name <- allLanguages bi ]
541
    unknownExtensions = [ name | bi <- allBuildInfo pkg
542
543
                               , UnknownExtension name <- allExtensions bi
                               , name `notElem` map display knownLanguages ]
544
545
    ourDeprecatedExtensions = nub $ catMaybes
      [ find ((==ext) . fst) deprecatedExtensions
546
      | bi <- allBuildInfo pkg
Ian Lynagh's avatar
Ian Lynagh committed
547
      , ext <- allExtensions bi ]
548
549
550
551
    languagesUsedAsExtensions =
      [ name | bi <- allBuildInfo pkg
             , UnknownExtension name <- allExtensions bi
             , name `elem` map display knownLanguages ]
552

553
    testedWithImpossibleRanges =
554
      [ Dependency (mkPackageName (display compiler)) vr
555
556
557
      | (compiler, vr) <- testedWith pkg
      , isNoVersion vr ]

558
    internalLibraries =
559
        map (maybe (packageName pkg) (unqualComponentNameToPackageName) . libName)
560
561
562
563
564
565
566
567
568
            (allLibraries pkg)
    buildDependsRangeOnInternalLibrary =
      [ dep
      | bi <- allBuildInfo pkg
      , dep@(Dependency name versionRange) <- targetBuildDepends bi
      , not (isAnyVersion versionRange)
      , name `elem` internalLibraries
      ]

569

570
571
572
573
checkLicense :: PackageDescription -> [PackageCheck]
checkLicense pkg =
  catMaybes [

574
    check (license pkg == UnspecifiedLicense) $
575
      PackageDistInexcusable
576
        "The 'license' field is missing."
577

578
579
580
  , check (license pkg == AllRightsReserved) $
      PackageDistSuspicious
        "The 'license' is AllRightsReserved. Is that really what you want?"
581
582
583
  , case license pkg of
      UnknownLicense l -> Just $
        PackageBuildWarning $
584
585
586
             quote ("license: " ++ l) ++ " is not a recognised license. The "
          ++ "known licenses are: "
          ++ commaSep (map display knownLicenses)
587
588
      _ -> Nothing

589
590
591
  , check (license pkg == BSD4) $
      PackageDistSuspicious $
           "Using 'license: BSD4' is almost always a misunderstanding. 'BSD4' "
592
593
        ++ "refers to the old 4-clause BSD license with the advertising "
        ++ "clause. 'BSD3' refers the new 3-clause BSD license."
594

595
596
597
598
599
600
601
602
603
604
  , 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

605
606
  , check (license pkg `notElem` [ AllRightsReserved
                                 , UnspecifiedLicense, PublicDomain]
607
           -- AllRightsReserved and PublicDomain are not strictly
608
           -- licenses so don't need license files.
Duncan Coutts's avatar
Duncan Coutts committed
609
        && null (licenseFiles pkg)) $
610
611
      PackageDistSuspicious "A 'license-file' is not specified."
  ]
612
613
614
615
616
617
618
  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 ]
619
620
621
    unknownLicenseVersion (AGPL (Just v))
      | v `notElem` knownVersions = Just knownVersions
      where knownVersions = [ v' | AGPL (Just v') <- knownLicenses ]
622
623
624
    unknownLicenseVersion (Apache  (Just v))
      | v `notElem` knownVersions = Just knownVersions
      where knownVersions = [ v' | Apache  (Just v') <- knownLicenses ]
625
    unknownLicenseVersion _ = Nothing
626

627
628
629
630
631
632
633
634
635
636
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
637
  , check (isNothing (repoType repo)) $
638
639
640
      PackageDistInexcusable
        "The source-repository 'type' is a required field."

EyalLotem's avatar
EyalLotem committed
641
  , check (isNothing (repoLocation repo)) $
642
643
644
      PackageDistInexcusable
        "The source-repository 'location' is a required field."

EyalLotem's avatar
EyalLotem committed
645
  , check (repoType repo == Just CVS && isNothing (repoModule repo)) $
646
647
648
      PackageDistInexcusable
        "For a CVS source-repository, the 'module' is a required field."

EyalLotem's avatar
EyalLotem committed
649
  , check (repoKind repo == RepoThis && isNothing (repoTag repo)) $
650
651
652
653
654
      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."

655
  , check (maybe False isAbsoluteOnAnyPlatform (repoSubdir repo)) $
656
657
658
659
660
661
662
      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.

663
664
665
666
checkGhcOptions :: PackageDescription -> [PackageCheck]
checkGhcOptions pkg =
  catMaybes [

667
    checkFlags ["-fasm"] $
668
      PackageDistInexcusable $
669
670
           "'ghc-options: -fasm' is unnecessary and will not work on CPU "
        ++ "architectures other than x86, x86-64, ppc or sparc."
671

Duncan Coutts's avatar
Duncan Coutts committed
672
673
  , checkFlags ["-fvia-C"] $
      PackageDistSuspicious $
674
675
676
677
           "'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
678
679
680

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

Duncan Coutts's avatar
Duncan Coutts committed
684
  , checkFlags ["-prof"] $
685
686
687
      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
688
        ++ "--enable-library-profiling and/or --enable-profiling."
Duncan Coutts's avatar
Duncan Coutts committed
689
690

  , checkFlags ["-o"] $
691
      PackageBuildWarning $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
692
693
           "'ghc-options: -o' is not needed. "
        ++ "The output files are named automatically."
Duncan Coutts's avatar
Duncan Coutts committed
694
695

  , checkFlags ["-hide-package"] $
696
      PackageBuildWarning $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
697
698
      "'ghc-options: -hide-package' is never needed. "
      ++ "Cabal hides all packages."
Duncan Coutts's avatar
Duncan Coutts committed
699

Duncan Coutts's avatar
Duncan Coutts committed
700
  , checkFlags ["--make"] $
701
      PackageBuildWarning $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
702
      "'ghc-options: --make' is never needed. Cabal uses this automatically."
Duncan Coutts's avatar
Duncan Coutts committed
703

Duncan Coutts's avatar
Duncan Coutts committed
704
705
  , checkFlags ["-main-is"] $
      PackageDistSuspicious $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
706
      "'ghc-options: -main-is' is not portable."
Duncan Coutts's avatar
Duncan Coutts committed
707
708

  , checkFlags ["-O0", "-Onot"] $
709
      PackageDistSuspicious $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
710
711
      "'ghc-options: -O0' is not needed. "
      ++ "Use the --disable-optimization configure flag."
Duncan Coutts's avatar
Duncan Coutts committed
712
713

  , checkFlags [ "-O", "-O1"] $
714
      PackageDistInexcusable $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
715
716
717
      "'ghc-options: -O' is not needed. "
      ++ "Cabal automatically adds the '-O' flag. "
      ++ "Setting it yourself interferes with the --disable-optimization flag."
718

Duncan Coutts's avatar
Duncan Coutts committed
719
  , checkFlags ["-O2"] $
720
      PackageDistSuspiciousWarn $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
721
722
723
      "'ghc-options: -O2' is rarely needed. "
      ++ "Check that it is giving a real benefit "
      ++ "and not just imposing longer compile times on your users."
724

Duncan Coutts's avatar
Duncan Coutts committed
725
  , checkFlags ["-split-objs"] $
726
      PackageBuildWarning $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
727
728
        "'ghc-options: -split-objs' is not needed. "
        ++ "Use the --enable-split-objs configure flag."
Duncan Coutts's avatar
Duncan Coutts committed
729

730
  , checkFlags ["-optl-Wl,-s", "-optl-s"] $
731
      PackageDistInexcusable $
732
733
734
735
736
737
           "'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
738
739
  , checkFlags ["-fglasgow-exts"] $
      PackageDistSuspicious $
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
740
741
        "Instead of 'ghc-options: -fglasgow-exts' it is preferable to use "
        ++ "the 'extensions' field."
Duncan Coutts's avatar
Duncan Coutts committed
742

743
  , check ("-threaded" `elem` lib_ghc_options) $
744
      PackageBuildWarning $
745
746
747
           "'ghc-options: -threaded' has no effect for libraries. It should "
        ++ "only be used for executables."

748
749
750
751
752
753
754
755
756
757
  , 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
758
  , checkAlternatives "ghc-options" "extensions"
759
760
      [ (flag, display extension) | flag <- all_ghc_options
                                  , Just extension <- [ghcExtension flag] ]
Duncan Coutts's avatar
Duncan Coutts committed
761
762

  , checkAlternatives "ghc-options" "extensions"
763
      [ (flag, extension) | flag@('-':'X':extension) <- all_ghc_options ]
Duncan Coutts's avatar
Duncan Coutts committed
764
765
766
767
768
769
770
771
772
773
774
775
776

  , 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 ]
777
778
779
780
781
782
783
784

  , 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) ]
785
786
787
  ]

  where
788
    all_ghc_options    = concatMap get_ghc_options (allBuildInfo pkg)
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
789
790
    lib_ghc_options    = concatMap (get_ghc_options . libBuildInfo)
                         (allLibraries pkg)
791
    get_ghc_options bi = hcOptions GHC bi ++ hcProfOptions GHC bi
792
                         ++ hcSharedOptions GHC bi
793

Duncan Coutts's avatar
Duncan Coutts committed
794
795
    checkFlags :: [String] -> PackageCheck -> Maybe PackageCheck
    checkFlags flags = check (any (`elem` flags) all_ghc_options)
Duncan Coutts's avatar
Duncan Coutts committed
796
797

    ghcExtension ('-':'f':name) = case name of
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
      "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
828
      _                                -> Nothing
Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
829
    ghcExtension "-cpp"             = enable CPP
Duncan Coutts's avatar
Duncan Coutts committed
830
831
    ghcExtension _                  = Nothing

Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
832
833
834
    enable  e = Just (EnableExtension e)
    disable e = Just (DisableExtension e)

Duncan Coutts's avatar
Duncan Coutts committed
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
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 ]
853
854
855
856
857
858
859

  , 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
860
861
862
863
864
865
866
  ]

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

867
868
869
        checkCCFlags :: [String] -> PackageCheck -> Maybe PackageCheck
        checkCCFlags flags = check (any (`elem` flags) all_ccOptions)

870
871
872
873
874
875
876
877
878
checkCPPOptions :: PackageDescription -> [PackageCheck]
checkCPPOptions pkg =
  catMaybes [
    checkAlternatives "cpp-options" "include-dirs"
      [ (flag, dir) | flag@('-':'I':dir) <- all_cppOptions]
    ]
  where all_cppOptions = [ opts | bi <- allBuildInfo pkg
                                , opts <- cppOptions bi ]

Mikhail Glushenkov's avatar
Mikhail Glushenkov committed
879
880
checkAlternatives :: String -> String -> [(String, String)]
                  -> Maybe PackageCheck
Duncan Coutts's avatar
Duncan Coutts committed
881
882
883
884
885
886
887
checkAlternatives badField goodField flags =
  check (not (null badFlags)) $
    PackageBuildWarning $
         "Instead of " ++ quote (badField ++ ": " ++ unwords badFlags)
      ++ " use " ++ quote (goodField ++ ": " ++ unwords goodFlags)

  where (badFlags, goodFlags) = unzip flags
888

889
890
checkPaths :: PackageDescription -> [PackageCheck]
checkPaths pkg =
891
892
893
894
895
896
897
898
  [ PackageBuildWarning $
         quote (kind ++ ": " ++ path)
      ++ " is a relative path outside of the source tree. "
      ++ "This will not work when generating a tarball with 'sdist'."
  | (path, kind) <- relPaths ++ absPaths
  , isOutsideTree path ]
  ++
  [ PackageDistInexcusable $
899
      quote (kind ++ ": " ++ path) ++ " is an absolute path."
900
  | (path, kind) <- relPaths
901
  , isAbsoluteOnAnyPlatform path ]
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
  ++
  [ PackageDistInexcusable $
         quote (kind ++ ": " ++ path) ++ " points inside the 'dist' "
      ++ "directory. This is not reliable because the location of this "
      ++ "directory is configurable by the user (or package manager). In "
      ++ "addition the layout of the 'dist' directory is subject to change "
      ++ "in future versions of Cabal."
  | (path, kind) <- relPaths ++ absPaths
  , isInsideDist path ]
  ++
  [ PackageDistInexcusable $
         "The 'ghc-options' contains the path '" ++ path ++ "' which points "
      ++ "inside the 'dist' directory. This is not reliable because the "
      ++ "location of this directory is configurable by the user (or package "
      ++ "manager). In addition the layout of the 'dist' directory is subject "
      ++ "to change in future versions of Cabal."
918
  | bi <- allBuildInfo pkg
919
920
921
922
923
924
925
926
927
928
929
930
931
932
  , (GHC, flags) <- options bi
  , path <- flags
  , isInsideDist path ]
  where
    isOutsideTree path = case splitDirectories path of
      "..":_     -> True
      ".":"..":_ -> True
      _          -> False
    isInsideDist path = case map lowercase (splitDirectories path) of
      "dist"    :_ -> True
      ".":"dist":_ -> True
      _            -> False
    -- paths that must be relative
    relPaths =
933
934
935
936
937
         [ (path, "extra-src-files") | path <- extraSrcFiles pkg ]
      ++ [ (path, "extra-tmp-files") | path <- extraTmpFiles pkg ]
      ++ [ (path, "extra-doc-files") | path <- extraDocFiles pkg ]
      ++ [ (path, "data-files")      | path <- dataFiles     pkg ]
      ++ [ (path, "data-dir")        | path <- [dataDir      pkg]]
938
      ++ [ (path, "license-file")    | path <- licenseFiles  pkg ]
939
940
      ++ concat
         [    [ (path, "c-sources")        | path <- cSources        bi ]
941
           ++ [ (path, "js-sources")       | path <- jsSources       bi ]
942
943
944
945
946
947
948
949
950
951
           ++ [ (path, "install-includes") | path <- installIncludes bi ]
           ++ [ (path, "hs-source-dirs")   | path <- hsSourceDirs    bi ]
         | bi <- allBuildInfo pkg ]
    -- paths that are allowed to be absolute
    absPaths = concat
      [    [ (path, "includes")         | path <- includes        bi ]
        ++ [ (path, "include-dirs")     | path <- includeDirs     bi ]
        ++ [ (path, "extra-lib-dirs")   | path <- extraLibDirs    bi ]
      | bi <- allBuildInfo pkg ]

Ian D. Bollinger's avatar
Ian D. Bollinger committed
952
--TODO: check sets of paths that would be interpreted differently between Unix
953
954
955
956
-- and windows, ie case-sensitive or insensitive. Things that might clash, or
-- conversely be distinguished.

--TODO: use the tar path checks on all the above paths
957

958
959
-- | Check that the package declares the version in the @\"cabal-version\"@
-- field correctly.
960
961
962
963
964
--
checkCabalVersion :: PackageDescription -> [PackageCheck]
checkCabalVersion pkg =
  catMaybes [

965
    -- check syntax of cabal-version field
966
    check (specVersion pkg >= mkVersion [1,10]
967
968
969
970
971
972
973
           && not simpleSpecVersionRangeSyntax) $
      PackageBuildWarning $
           "Packages relying on Cabal 1.10 or later must only specify a "
        ++ "version range of the form 'cabal-version: >= x.y'. Use "
        ++ "'cabal-version: >= " ++ display (specVersion pkg) ++ "'."

    -- check syntax of cabal-version field
974
  , check (specVersion pkg < mkVersion [1,9]
975
976
977
978
979
980
981
982
983
984
985
986
987
988
           && not simpleSpecVersionRangeSyntax) $
      PackageDistSuspicious $
           "It is recommended that the 'cabal-version' field only specify a "
        ++ "version range of the form '>= x.y'. Use "
        ++ "'cabal-version: >= " ++ display (specVersion pkg) ++ "'. "
        ++ "Tools based on Cabal 1.10 and later will ignore upper bounds."

    -- check syntax of cabal-version field
  , checkVersion [1,12] simpleSpecVersionSyntax $
      PackageBuildWarning $
           "With Cabal 1.10 or earlier, the 'cabal-version' field must use "
        ++ "range syntax rather than a simple version number. Use "
        ++ "'cabal-version: >= " ++ display (specVersion pkg) ++ "'."

989
990
    -- check use of test suite sections
  , checkVersion [1,8] (not (null $ testSuites pkg)) $
991
      PackageDistInexcusable $
992
993
           "The 'test-suite' section is new in Cabal 1.10. "
        ++ "Unfortunately it messes up the parser in older Cabal versions "
Duncan Coutts's avatar
Duncan Coutts committed
994
        ++ "so you must specify at least 'cabal-version: >= 1.8', but note "
995
        ++ "that only Cabal 1.10 and later can actually run such test suites."
996

997
998
999
1000
    -- check use of default-language field
    -- note that we do not need to do an equivalent check for the
    -- other-language field since that one does not change behaviour
  , checkVersion [1,10] (any isJust (buildInfoField defaultLanguage)) $