@@ -47,6 +47,7 @@ module Distribution.PackageDescription.Check (
47
47
) where
48
48
49
49
import Data.Maybe (isNothing , catMaybes )
50
+ import Data.List (intersperse , sort , group , isPrefixOf )
50
51
import System.Directory (doesFileExist )
51
52
52
53
import Distribution.PackageDescription
@@ -56,6 +57,7 @@ import Distribution.Simple.Utils (cabalVersion)
56
57
57
58
import Distribution.Version (Version (.. ), withinRange , showVersionRange )
58
59
import Distribution.Package (PackageIdentifier (.. ))
60
+ import Language.Haskell.Extension (Extension (.. ))
59
61
import System.FilePath (takeExtension , (</>) )
60
62
61
63
-- | Results of some kind of failed package check.
@@ -100,8 +102,6 @@ check True pc = Just pc
100
102
-- * Standard checks
101
103
-- ------------------------------------------------------------
102
104
103
- -- TODO: give hints about old extentions. see Simple.GHC, reverse mapping
104
- -- TODO: and allmost ghc -X flags should be extensions
105
105
-- TODO: Once we implement striping (ticket #88) we should also reject
106
106
-- ghc-options: -optl-Wl,-s.
107
107
@@ -117,6 +117,7 @@ checkPackage pkg =
117
117
++ checkFields pkg
118
118
++ checkLicense pkg
119
119
++ checkGhcOptions pkg
120
+ ++ checkCCOptions pkg
120
121
121
122
122
123
-- ------------------------------------------------------------
@@ -160,8 +161,17 @@ checkLibrary lib =
160
161
check (buildable (libBuildInfo lib) && null (exposedModules lib)) $
161
162
PackageBuildImpossible
162
163
" A library was specified, but no 'exposed-modules' list has been given."
164
+
165
+ , check (not (null moduleDuplicates)) $
166
+ PackageBuildWarning $
167
+ " Dulicate modules in library: " ++ commaSep moduleDuplicates
163
168
]
164
169
170
+ where moduleDuplicates = [ module_
171
+ | let modules = exposedModules lib
172
+ ++ otherModules (libBuildInfo lib)
173
+ , (module_: _: _) <- group (sort modules) ]
174
+
165
175
checkExecutable :: Executable -> [PackageCheck ]
166
176
checkExecutable exe =
167
177
catMaybes [
@@ -175,8 +185,16 @@ checkExecutable exe =
175
185
PackageBuildImpossible $
176
186
" The 'Main-Is' field must specify a '.hs' or '.lhs' file\n "
177
187
++ " (even if it is generated by a preprocessor)."
188
+
189
+ , check (not (null moduleDuplicates)) $
190
+ PackageBuildWarning $
191
+ " Dulicate modules in executable '" ++ exeName exe ++ " ': "
192
+ ++ commaSep moduleDuplicates
178
193
]
179
194
195
+ where moduleDuplicates = [ module_
196
+ | let modules = otherModules (buildInfo exe)
197
+ , (module_: _: _) <- group (sort modules) ]
180
198
181
199
-- ------------------------------------------------------------
182
200
-- * Additional pure checks
@@ -233,24 +251,80 @@ checkGhcOptions pkg =
233
251
" 'ghc-options: -Werror' makes the package easy to "
234
252
++ " break with future GHC versions."
235
253
236
- , checkFlag " -fasm" $
254
+ , checkFlags [ " -fasm" ] $
237
255
PackageDistInexcusable $
238
256
" 'ghc-options: -fasm' is unnecessary and breaks on all "
239
257
++ " arches except for x86, x86-64 and ppc."
240
258
241
- , checkFlag " -O" $
259
+ , checkFlags [" -fvia-C" ] $
260
+ PackageDistSuspicious $
261
+ " 'ghc-options: -fvia-C' is usually unnecessary."
262
+
263
+ , checkFlags [" -fhpc" ] $
264
+ PackageDistInexcusable $
265
+ " 'ghc-options: -fhpc' is not appropriate for a distributed package."
266
+
267
+ , check (any (" -d" `isPrefixOf` ) all_ghc_options) $
268
+ PackageDistInexcusable $
269
+ " 'ghc-options: -d*' debug flags are not appropriate for a distributed package."
270
+
271
+ , checkFlags [" -prof" ] $
272
+ PackageDistInexcusable $
273
+ " 'ghc-options: -prof' is not needed. Use the --enable-library-profiling configure flag."
274
+
275
+ , checkFlags [" -o" ] $
276
+ PackageDistInexcusable $
277
+ " 'ghc-options: -o' is not allowed. The output files are named automatically."
278
+
279
+ , checkFlags [" -hide-package" ] $
280
+ PackageDistInexcusable $
281
+ " 'ghc-options: -hide-package' is never needed. Cabal hides all packages\n "
282
+
283
+ , checkFlags [" -main-is" ] $
284
+ PackageDistSuspicious $
285
+ " 'ghc-options: -main-is' is not portable."
286
+
287
+ , checkFlags [" -O0" , " -Onot" ] $
288
+ PackageDistInexcusable $
289
+ " 'ghc-options: -O0' is not needed. Use the --disable-optimization configure flag."
290
+
291
+ , checkFlags [ " -O" , " -O1" ] $
242
292
PackageDistInexcusable $
243
293
" 'ghc-options: -O' is not needed. Cabal automatically adds the '-O' flag.\n "
244
294
++ " Setting it yourself interferes with the --disable-optimization flag."
245
295
246
- , checkFlag " -O2" $
296
+ , checkFlags [ " -O2" ] $
247
297
PackageDistSuspicious $
248
298
" 'ghc-options: -O2' is rarely needed. Check that it is giving a real benefit\n "
249
299
++ " and not just imposing longer compile times on your users."
250
300
251
- , check ( any ( `elem` all_ghc_options) [" -ffi " , " -fffi " ]) $
301
+ , checkFlags [" -split-objs " ] $
252
302
PackageDistInexcusable $
253
- " Instead of using -ffi or -fffi, use 'extensions: ForeignFunctionInterface'"
303
+ " 'ghc-options: -split-objs' is not needed. Use the --enable-split-objs configure flag."
304
+
305
+ , checkFlags [" -fglasgow-exts" ] $
306
+ PackageDistSuspicious $
307
+ " Instead of 'ghc-options: -fglasgow-exts' it is preferable to use the 'extensions' field."
308
+
309
+ , checkAlternatives " ghc-options" " extensions"
310
+ [ (flag, show extension) | flag <- all_ghc_options
311
+ , Just extension <- [ghcExtension flag] ]
312
+
313
+ , checkAlternatives " ghc-options" " extensions"
314
+ [ (flag, extension) | flag@ (' -' : ' X' : extension) <- all_ghc_options ]
315
+
316
+ , checkAlternatives " ghc-options" " cpp-options" $
317
+ [ (flag, flag) | flag@ (' -' : ' D' : _) <- all_ghc_options ]
318
+ ++ [ (flag, flag) | flag@ (' -' : ' U' : _) <- all_ghc_options ]
319
+
320
+ , checkAlternatives " ghc-options" " include-dirs"
321
+ [ (flag, dir) | flag@ (' -' : ' I' : dir) <- all_ghc_options ]
322
+
323
+ , checkAlternatives " ghc-options" " extra-libraries"
324
+ [ (flag, lib) | flag@ (' -' : ' l' : lib) <- all_ghc_options ]
325
+
326
+ , checkAlternatives " ghc-options" " extra-lib-dirs"
327
+ [ (flag, dir) | flag@ (' -' : ' L' : dir) <- all_ghc_options ]
254
328
]
255
329
256
330
where
@@ -263,8 +337,63 @@ checkGhcOptions pkg =
263
337
, (GHC , strs) <- options bi ]
264
338
all_ghc_options = concat ghc_options
265
339
266
- checkFlag :: String -> PackageCheck -> Maybe PackageCheck
267
- checkFlag flag = check (flag `elem` all_ghc_options)
340
+ checkFlags :: [String ] -> PackageCheck -> Maybe PackageCheck
341
+ checkFlags flags = check (any (`elem` flags) all_ghc_options)
342
+
343
+ ghcExtension (' -' : ' f' : name) = case name of
344
+ " allow-overlapping-instances" -> Just OverlappingInstances
345
+ " th" -> Just TemplateHaskell
346
+ " ffi" -> Just ForeignFunctionInterface
347
+ " fi" -> Just ForeignFunctionInterface
348
+ " no-monomorphism-restriction" -> Just NoMonomorphismRestriction
349
+ " no-mono-pat-binds" -> Just NoMonoPatBinds
350
+ " allow-undecidable-instances" -> Just UndecidableInstances
351
+ " allow-incoherent-instances" -> Just IncoherentInstances
352
+ " arrows" -> Just Arrows
353
+ " generics" -> Just Generics
354
+ " no-implicit-prelude" -> Just NoImplicitPrelude
355
+ " implicit-params" -> Just ImplicitParams
356
+ " bang-patterns" -> Just BangPatterns
357
+ " scoped-type-variables" -> Just ScopedTypeVariables
358
+ " extended-default-rules" -> Just ExtendedDefaultRules
359
+ _ -> Nothing
360
+ ghcExtension (' -' : ' c' : " pp" ) = Just CPP
361
+ ghcExtension _ = Nothing
362
+
363
+
364
+ checkCCOptions :: PackageDescription -> [PackageCheck ]
365
+ checkCCOptions pkg =
366
+ catMaybes [
367
+
368
+ checkAlternatives " cc-options" " include-dirs"
369
+ [ (flag, dir) | flag@ (' -' : ' I' : dir) <- all_ccOptions ]
370
+
371
+ , checkAlternatives " cc-options" " extra-libraries"
372
+ [ (flag, lib) | flag@ (' -' : ' l' : lib) <- all_ccOptions ]
373
+
374
+ , checkAlternatives " cc-options" " extra-lib-dirs"
375
+ [ (flag, dir) | flag@ (' -' : ' L' : dir) <- all_ccOptions ]
376
+
377
+ , checkAlternatives " ld-options" " extra-libraries"
378
+ [ (flag, lib) | flag@ (' -' : ' l' : lib) <- all_ldOptions ]
379
+
380
+ , checkAlternatives " ld-options" " extra-lib-dirs"
381
+ [ (flag, dir) | flag@ (' -' : ' L' : dir) <- all_ldOptions ]
382
+ ]
383
+
384
+ where all_ccOptions = [ opts | bi <- allBuildInfo pkg
385
+ , opts <- ccOptions bi ]
386
+ all_ldOptions = [ opts | bi <- allBuildInfo pkg
387
+ , opts <- ldOptions bi ]
388
+
389
+ checkAlternatives :: String -> String -> [(String , String )] -> Maybe PackageCheck
390
+ checkAlternatives badField goodField flags =
391
+ check (not (null badFlags)) $
392
+ PackageBuildWarning $
393
+ " Instead of " ++ quote (badField ++ " : " ++ unwords badFlags)
394
+ ++ " use " ++ quote (goodField ++ " : " ++ unwords goodFlags)
395
+
396
+ where (badFlags, goodFlags) = unzip flags
268
397
269
398
-- ------------------------------------------------------------
270
399
-- * Checks in IO
@@ -275,9 +404,11 @@ checkGhcOptions pkg =
275
404
--
276
405
checkPackageFiles :: PackageDescription -> FilePath -> IO [PackageCheck ]
277
406
checkPackageFiles pkg root = do
278
- licenseError <- checkLicenseExists pkg root
407
+ licenseError <- checkLicenseExists pkg root
408
+ setupError <- checkSetupExists pkg root
409
+ configureError <- checkConfigureExists pkg root
279
410
280
- return (catMaybes [licenseError])
411
+ return (catMaybes [licenseError, setupError, configureError ])
281
412
282
413
checkLicenseExists :: PackageDescription -> FilePath -> IO (Maybe PackageCheck )
283
414
checkLicenseExists pkg root
@@ -291,4 +422,29 @@ checkLicenseExists pkg root
291
422
292
423
where
293
424
file = licenseFile pkg
294
- quote s = [' "' ] ++ s ++ [' "' ]
425
+
426
+ checkSetupExists :: PackageDescription -> FilePath -> IO (Maybe PackageCheck )
427
+ checkSetupExists _ root = do
428
+ hsexists <- doesFileExist (root </> " Setup.hs" )
429
+ lhsexists <- doesFileExist (root </> " Setup.lhs" )
430
+ return $ check (not hsexists && not lhsexists) $
431
+ PackageDistInexcusable $
432
+ " The package is missing a Setup.hs or Setup.lhs script."
433
+
434
+ checkConfigureExists :: PackageDescription -> FilePath -> IO (Maybe PackageCheck )
435
+ checkConfigureExists PackageDescription { buildType = Just Configure } root = do
436
+ exists <- doesFileExist (root </> " configure" )
437
+ return $ check (not exists) $
438
+ PackageBuildWarning $
439
+ " The 'build-type' is 'Configure' but there is no 'configure' script."
440
+ checkConfigureExists _ _ = return Nothing
441
+
442
+ -- ------------------------------------------------------------
443
+ -- * Utils
444
+ -- ------------------------------------------------------------
445
+
446
+ quote :: String -> String
447
+ quote s = " '" ++ s ++ " '"
448
+
449
+ commaSep :: [String ] -> String
450
+ commaSep = concat . intersperse " ,"
0 commit comments