Program.hs 23.8 KB
Newer Older
1
{-# OPTIONS -cpp #-}
ijones's avatar
ijones committed
2
3
-----------------------------------------------------------------------------
-- |
4
-- Module      :  Distribution.Simple.Program
ijones's avatar
ijones committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- Copyright   :  Isaac Jones 2006
-- 
-- Maintainer  :  Isaac Jones <ijones@syntaxpolice.org>
-- Stability   :  alpha
-- Portability :  GHC, Hugs
--
-- Explanation: A program is basically a name, a location, and some
-- arguments.
--
-- One nice thing about using it is that any program that is
-- registered with Cabal will get some \"configure\" and \".cabal\"
-- helpers like --with-foo-args --foo-path= and extra-foo-args.
--
-- There's also good default behavior for trying to find \"foo\" in
-- PATH, being able to override its location, etc.
--
-- There's also a hook for adding programs in a Setup.lhs script.  See
-- hookedPrograms in 'Distribution.Simple.UserHooks'.  This gives a
-- hook user the ability to get the above flags and such so that they
-- don't have to write all the PATH logic inside Setup.lhs.

26
module Distribution.Simple.Program (
27
28
29
    -- * Program and functions for constructing them
      Program(..)
    , simpleProgram
30
    , findProgramOnPath
31
32
33
34
35
36
37
38
    , findProgramVersion

    -- * Configured program and related functions
    , ConfiguredProgram(..)
    , programPath
    , ProgArg
    , ProgramLocation(..)
    , rawSystemProgram
39
    , rawSystemProgramStdout
40
41
42
43
44
45
46
47
48

    -- * The collection of unconfigured and configured progams
    , builtinPrograms

    -- * The collection of configured programs we can run
    , ProgramConfiguration
    , emptyProgramConfiguration
    , defaultProgramConfiguration
    , addKnownProgram
49
    , lookupKnownProgram
50
51
52
53
    , knownPrograms
    , userSpecifyPath
    , userMaybeSpecifyPath
    , userSpecifyArgs
54
    , userSpecifiedArgs
55
56
57
58
59
    , lookupProgram
    , updateProgram
    , configureAllKnownPrograms
    , requireProgram
    , rawSystemProgramConf
60
    , rawSystemProgramStdoutConf
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

    -- * Programs that Cabal knows about
    , ghcProgram
    , ghcPkgProgram
    , nhcProgram
    , hmakeProgram
    , jhcProgram
    , hugsProgram
    , ffihugsProgram
    , ranlibProgram
    , arProgram
    , happyProgram
    , alexProgram
    , hsc2hsProgram
    , c2hsProgram
    , cpphsProgram
    , hscolourProgram
    , haddockProgram
    , greencardProgram
    , ldProgram
    , tarProgram
    , cppProgram
    , pfesetupProgram
84
    , pkgConfigProgram
85
    ) where
ijones's avatar
ijones committed
86

87
import qualified Distribution.Compat.Map as Map
88
import Distribution.Compat.Directory (findExecutable)
89
90
91
92
import Distribution.Compat.TempFile (withTempFile)
import Distribution.Simple.Utils (die, debug, warn, rawSystemExit,
                                  rawSystemStdout, rawSystemStdout')
import Distribution.Version (Version(..), readVersion, showVersion,
93
                             VersionRange(..), withinRange, showVersionRange)
94
import Distribution.Verbosity
95
96
import System.Directory (doesFileExist, removeFile)
import System.FilePath  (dropExtension)
97
import System.IO.Error (try)
98
import Control.Monad (join, foldM)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import Control.Exception as Exception (catch)

-- | Represents a program which can be configured.
data Program = Program {
        -- | The simple name of the program, eg. ghc
        programName :: String,
        
        -- | A function to search for the program if it's location was not
        -- specified by the user. Usually this will just be a 
        programFindLocation :: Verbosity -> IO (Maybe FilePath),
        
        -- | Try to find the version of the program. For many programs this is
        -- not possible or is not necessary so it's ok to return Nothing.
        programFindVersion :: Verbosity -> FilePath -> IO (Maybe Version)
    }

type ProgArg = String

data ConfiguredProgram = ConfiguredProgram {
        -- | Just the name again
        programId :: String,
        
        -- | The version of this program, if it is known.
        programVersion :: Maybe Version,

        -- | Default command-line args for this program.
        -- These flags will appear first on the command line, so they can be
        -- overridden by subsequent flags.
        programArgs :: [ProgArg],

        -- | Location of the program. eg. @\/usr\/bin\/ghc-6.4@
        programLocation :: ProgramLocation    
    } deriving (Read, Show)

-- | Where a program was found. Also tells us whether it's specifed by user or
134
-- not.  This includes not just the path, but the program as well.
135
136
data ProgramLocation
    = UserSpecified { locationPath :: FilePath }
nominolo@gmail.com's avatar
nominolo@gmail.com committed
137
138
      -- ^The user gave the path to this program,
      -- eg. --ghc-path=\/usr\/bin\/ghc-6.6
139
    | FoundOnSystem { locationPath :: FilePath }
nominolo@gmail.com's avatar
nominolo@gmail.com committed
140
      -- ^The location of the program, as located by searching PATH.
ijones's avatar
ijones committed
141
      deriving (Read, Show)
142

143
144
145
-- ------------------------------------------------------------
-- * Programs functions
-- ------------------------------------------------------------
146

147
148
149
-- | The full path of a configured program.
programPath :: ConfiguredProgram -> FilePath
programPath = locationPath . programLocation
ijones's avatar
ijones committed
150

151
152
153
154
155
156
157
158
159
-- | Make a simple named program.
--
-- By default we'll just search for it in the path and not try to find the
-- version name. You can override these behaviours if necessary, eg:
--
-- > simpleProgram "foo" { programFindLocation = ... , programFindVersion ... }
--
simpleProgram :: String -> Program
simpleProgram name = 
160
  Program name (findProgramOnPath name) (\_ _ -> return Nothing)
161
162

-- | Look for a program on the path.
163
164
findProgramOnPath :: FilePath -> Verbosity -> IO (Maybe FilePath)
findProgramOnPath prog verbosity = do
165
  debug verbosity $ "searching for " ++ prog ++ " in path."
166
  res <- findExecutable prog
167
168
169
  case res of
      Nothing   -> debug verbosity ("Cannot find " ++ prog ++ " on the path")
      Just path -> debug verbosity ("found " ++ prog ++ " at "++ path)
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  return res

-- | Look for a program and try to find it's version number. It can accept
-- either an absolute path or the name of a program binary, in which case we
-- will look for the program on the path.
--
findProgramVersion :: ProgArg            -- ^ version args
                   -> (String -> String) -- ^ function to select version
                                         --   number from program output
                   -> Verbosity
                   -> FilePath           -- ^ location
                   -> IO (Maybe Version)
findProgramVersion versionArg selectVersion verbosity path = do
  str <- rawSystemStdout verbosity path [versionArg]
         `Exception.catch` \_ -> return ""
  let version = readVersion (selectVersion str)
  case version of
187
188
189
      Nothing -> warn verbosity $ "cannot determine version of " ++ path
                               ++ " :\n" ++ show str
      Just v  -> debug verbosity $ path ++ " is version " ++ showVersion v
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  return version

-- ------------------------------------------------------------
-- * Programs database
-- ------------------------------------------------------------

-- | The configuration is a collection of information about programs. It
-- contains information both about configured programs and also about programs
-- that we are yet to configure.
--
-- The idea is that we start from a collection of unconfigured programs and one
-- by one we try to configure them at which point we move them into the
-- configured collection. For unconfigured programs we record not just the
-- 'Program' but also any user-provided arguments and location for the program.
data ProgramConfiguration = ProgramConfiguration {
        unconfiguredProgs :: UnconfiguredProgs,
        configuredProgs   :: ConfiguredProgs
    }
type UnconfiguredProgram = (Program, Maybe FilePath, [ProgArg])
type UnconfiguredProgs = Map.Map String UnconfiguredProgram
type ConfiguredProgs =   Map.Map String ConfiguredProgram

emptyProgramConfiguration :: ProgramConfiguration
emptyProgramConfiguration = ProgramConfiguration Map.empty Map.empty

defaultProgramConfiguration :: ProgramConfiguration
defaultProgramConfiguration =
  foldl (flip addKnownProgram) emptyProgramConfiguration builtinPrograms

-- internal helpers:
updateUnconfiguredProgs :: (UnconfiguredProgs -> UnconfiguredProgs)
                        -> ProgramConfiguration -> ProgramConfiguration
updateUnconfiguredProgs update conf =
  conf { unconfiguredProgs = update (unconfiguredProgs conf) }
updateConfiguredProgs :: (ConfiguredProgs -> ConfiguredProgs)
                      -> ProgramConfiguration -> ProgramConfiguration
updateConfiguredProgs update conf =
  conf { configuredProgs = update (configuredProgs conf) }

-- Read & Show instances are based on listToFM
-- Note that we only serialise the configured part of the database, this is
-- because we don't need the unconfigured part after the configure stage, and
-- additionally because we cannot read/show 'Program' as it contains functions.
233
instance Show ProgramConfiguration where
234
  show = show . Map.toAscList . configuredProgs
235
236

instance Read ProgramConfiguration where
237
238
239
  readsPrec p s =
    [ (emptyProgramConfiguration { configuredProgs = Map.fromList s' }, r)
    | (s', r) <- readsPrec p s ]
ijones's avatar
ijones committed
240

241
242
-- -------------------------------
-- Managing unconfigured programs
ijones's avatar
ijones committed
243

244
245
246
247
248
-- | Add a known program that we may configure later
addKnownProgram :: Program -> ProgramConfiguration -> ProgramConfiguration
addKnownProgram prog = updateUnconfiguredProgs $
  Map.insert (programName prog) (prog, Nothing, [])

249
250
251
252
lookupKnownProgram :: String -> ProgramConfiguration -> Maybe Program
lookupKnownProgram name =
  fmap (\(p,_,_)->p) . Map.lookup name . unconfiguredProgs

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
knownPrograms :: ProgramConfiguration -> [(Program, Maybe ConfiguredProgram)]
knownPrograms conf = 
  [ (p,p') | (p,_,_) <- Map.elems (unconfiguredProgs conf)
           , let p' = Map.lookup (programName p) (configuredProgs conf) ]

-- |User-specify this path.  Basically override any path information
-- for this program in the configuration. If it's not a known
-- program ignore it.
userSpecifyPath :: String   -- ^Program name
                -> FilePath -- ^user-specified path to the program
                -> ProgramConfiguration -> ProgramConfiguration
userSpecifyPath name path = updateUnconfiguredProgs $
  flip Map.update name $ \(prog, _, args) -> Just (prog, Just path, args)

userMaybeSpecifyPath :: String -> Maybe FilePath
                     -> ProgramConfiguration -> ProgramConfiguration
userMaybeSpecifyPath _    Nothing conf     = conf
userMaybeSpecifyPath name (Just path) conf = userSpecifyPath name path conf

-- |User-specify the arguments for this program.  Basically override
-- any args information for this program in the configuration. If it's
-- not a known program, ignore it..
userSpecifyArgs :: String    -- ^Program name
                -> [ProgArg] -- ^user-specified args
                -> ProgramConfiguration
                -> ProgramConfiguration
279
280
281
282
283
284
285
userSpecifyArgs name args' =
    updateUnconfiguredProgs
      (flip Map.update name $
         \(prog, path, args) -> Just (prog, path, args ++ args'))
  . updateConfiguredProgs
      (flip Map.update name $
         \prog -> Just prog { programArgs = programArgs prog ++ args' })
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314

userSpecifiedPath :: Program -> ProgramConfiguration -> Maybe FilePath
userSpecifiedPath prog =
  join . fmap (\(_,p,_)->p) . Map.lookup (programName prog) . unconfiguredProgs

userSpecifiedArgs :: Program -> ProgramConfiguration -> [ProgArg]
userSpecifiedArgs prog =
  maybe [] (\(_,_,as)->as) . Map.lookup (programName prog) . unconfiguredProgs

-- -----------------------------
-- Managing configured programs

-- | Try to find a configured program
lookupProgram :: Program -> ProgramConfiguration -> Maybe ConfiguredProgram
lookupProgram prog = Map.lookup (programName prog) . configuredProgs

-- | Update a configured program in the database.
updateProgram :: ConfiguredProgram -> ProgramConfiguration
                                   -> ProgramConfiguration
updateProgram prog = updateConfiguredProgs $
  Map.insert (programId prog) prog

-- ---------------------------
-- Configuring known programs

-- | Try to configure a specific program. If the program is already included in
-- the colleciton of unconfigured programs then we use any user-supplied
-- location and arguments. If the program gets configured sucessfully it gets 
-- added to the configured collection.
315
--
316
317
318
-- Note that it is not a failure if the program cannot be configured. It's only
-- a failure if the user supplied a location and the program could not be found
-- at that location.
319
--
320
321
322
323
324
325
326
327
328
329
330
331
332
333
-- The reason for it not being a failure at this stage is that we don't know up
-- front all the programs we will need, so we try to configure them all.
-- To verify that a program was actually sucessfully configured use
-- 'requireProgram'. 
--
configureProgram :: Verbosity
                 -> Program
                 -> ProgramConfiguration
                 -> IO ProgramConfiguration
configureProgram verbosity prog conf = do
  let name = programName prog
  maybeLocation <- case userSpecifiedPath prog conf of
    Nothing   -> programFindLocation prog verbosity
             >>= return . fmap FoundOnSystem
334
335
336
    Just path -> do
      absolute <- doesFileExist path
      if absolute
337
        then return (Just (UserSpecified path))
338
        else findProgramOnPath path verbosity
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
         >>= maybe (die notFound) (return . Just . UserSpecified)
      where notFound = "Cannot find " ++ name ++ " at "
                     ++ path ++ " or on the path"
  case maybeLocation of
    Nothing -> return conf
    Just location -> do
      version <- programFindVersion prog verbosity (locationPath location)
      let configuredProg = ConfiguredProgram {
            programId       = name,
            programVersion  = version,
            programArgs     = userSpecifiedArgs prog conf,
            programLocation = location
          }
      return (updateConfiguredProgs (Map.insert name configuredProg) conf)

-- | Try to configure all the known programs that have not yet been configured.
configureAllKnownPrograms :: Verbosity
                  -> ProgramConfiguration
                  -> IO ProgramConfiguration
configureAllKnownPrograms verbosity conf =
  foldM (flip (configureProgram verbosity)) conf
    [ prog | (prog,_,_) <- Map.elems (unconfiguredProgs conf
                     `Map.difference` configuredProgs conf) ]

-- | Check that a program is configured and available to be run.
--
-- Additionally check that the version of the program number is suitable.
-- For example 'AnyVersion' or @'orLaterVersion' ('Version' [1,0] [])@
--
-- It raises an exception if the program could not be configured or the version
-- is unsuitable, otherwise it returns the configured program.
requireProgram :: Verbosity -> Program -> VersionRange -> ProgramConfiguration
               -> IO (ConfiguredProgram, ProgramConfiguration)
requireProgram verbosity prog range conf = do
  
  -- If it's not already been configured, try to configure it now
  conf' <- case lookupProgram prog conf of
    Nothing -> configureProgram verbosity prog conf
    Just _  -> return conf
  
  case lookupProgram prog conf' of
    Nothing                           -> die notFound
    Just configuredProg
      | range == AnyVersion           -> return (configuredProg, conf')
    Just configuredProg@ConfiguredProgram { programLocation = location } ->
      case programVersion configuredProg of
        Just version
          | withinRange version range -> return (configuredProg, conf')
          | otherwise                 -> die (badVersion version location)
        Nothing                       -> die (noVersion location)

  where notFound       = programName prog ++ versionRequirement
                      ++ " is required but it could not be found."
        badVersion v l = programName prog ++ versionRequirement
                      ++ " is required but the version found at "
                      ++ locationPath l ++ " is version " ++ showVersion v
        noVersion l    = programName prog ++ versionRequirement
                      ++ " is required but the version of "
                      ++ locationPath l ++ " could not be determined."
        versionRequirement
          | range == AnyVersion = ""
          | otherwise           = " version " ++ showVersionRange range
401

402
403
404
-- ------------------------------------------------------------
-- * Running programs
-- ------------------------------------------------------------
405

406
407
408
409
410
411
412
413
-- | Runs the given configured program.
rawSystemProgram :: Verbosity          -- ^Verbosity
                 -> ConfiguredProgram  -- ^The program to run
                 -> [ProgArg]          -- ^Any /extra/ arguments to add
                 -> IO ()
rawSystemProgram verbosity prog extraArgs
  = rawSystemExit verbosity (programPath prog) (programArgs prog ++ extraArgs)

414
415
416
417
418
419
420
421
-- | Runs the given configured program and gets the output.
rawSystemProgramStdout :: Verbosity          -- ^Verbosity
                       -> ConfiguredProgram  -- ^The program to run
                       -> [ProgArg]          -- ^Any /extra/ arguments to add
                       -> IO String
rawSystemProgramStdout verbosity prog extraArgs
  = rawSystemStdout verbosity (programPath prog) (programArgs prog ++ extraArgs)

422
423
424
425
426
427
428
429
-- | Looks up the given program in the program configuration and runs it.
rawSystemProgramConf :: Verbosity            -- ^verbosity
                     -> Program              -- ^The program to run
                     -> ProgramConfiguration -- ^look up the program here
                     -> [ProgArg]            -- ^Any /extra/ arguments to add
                     -> IO ()
rawSystemProgramConf verbosity prog programConf extraArgs =
  case lookupProgram prog programConf of
430
    Nothing -> die ("The program " ++ programName prog ++ " is required but it could not be found")
431
    Just configuredProg -> rawSystemProgram verbosity configuredProg extraArgs
432

433
434
435
436
437
438
439
440
-- | Looks up the given program in the program configuration and runs it.
rawSystemProgramStdoutConf :: Verbosity            -- ^verbosity
                           -> Program              -- ^The program to run
                           -> ProgramConfiguration -- ^look up the program here
                           -> [ProgArg]            -- ^Any /extra/ arguments to add
                           -> IO String
rawSystemProgramStdoutConf verbosity prog programConf extraArgs =
  case lookupProgram prog programConf of
441
    Nothing -> die ("The program " ++ programName prog ++ " is required but it could not be found")
442
443
    Just configuredProg -> rawSystemProgramStdout verbosity configuredProg extraArgs

ijones's avatar
ijones committed
444
-- ------------------------------------------------------------
445
-- * Known programs
ijones's avatar
ijones committed
446
447
-- ------------------------------------------------------------

448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
-- | The default list of programs.
-- These programs are typically used internally to Cabal.
builtinPrograms :: [Program]
builtinPrograms =
    [
    -- compilers and related progs
      ghcProgram
    , ghcPkgProgram
    , hugsProgram
    , ffihugsProgram
    , nhcProgram
    , hmakeProgram
    , jhcProgram
    -- preprocessors
    , hscolourProgram
    , haddockProgram
    , happyProgram
    , alexProgram
    , hsc2hsProgram
    , c2hsProgram
    , cpphsProgram
    , greencardProgram
    , pfesetupProgram
    -- platform toolchain
    , ranlibProgram
    , arProgram
    , ldProgram
    , tarProgram
476
477
    -- configuration tools
    , pkgConfigProgram
478
479
    ]

ijones's avatar
ijones committed
480
ghcProgram :: Program
481
482
483
ghcProgram = (simpleProgram "ghc") {
    programFindVersion = findProgramVersion "--numeric-version" id
  }
ijones's avatar
ijones committed
484
485

ghcPkgProgram :: Program
486
487
488
489
490
491
492
493
ghcPkgProgram = (simpleProgram "ghc-pkg") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      -- Invoking "ghc-pkg --version" gives a string like
      -- "GHC package manager version 6.4.1"
      case words str of
        (_:_:_:_:ver:_) -> ver
        _               -> ""
  }
ijones's avatar
ijones committed
494
495

nhcProgram :: Program
496
497
498
499
500
501
502
503
504
nhcProgram = (simpleProgram "nhc98") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      -- Invoking "nhc98 --version" gives a string like
      -- /usr/local/bin/nhc98: v1.20 (2007-11-22)
      -- [ config: ix86-Linux/ghc by user@host on Fri Nov 23 14:11:17 GMT 2007 ]
      case words str of
        (_:('v':ver):_) -> ver
        _               -> ""
  }
ijones's avatar
ijones committed
505

506
507
508
509
510
511
512
hmakeProgram :: Program
hmakeProgram = (simpleProgram "hmake") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      case words str of
        (_:ver:_) -> ver
        _         -> ""
  }
ekarttun's avatar
ekarttun committed
513

514
515
516
517
518
519
520
521
522
jhcProgram :: Program
jhcProgram = (simpleProgram "jhc") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      case words str of
        (_:ver:_) -> ver
        _         -> ""
  }

-- AArgh! Finding the version of hugs or ffihugs is almost impossible.
ijones's avatar
ijones committed
523
524
525
hugsProgram :: Program
hugsProgram = simpleProgram "hugs"

526
527
528
ffihugsProgram :: Program
ffihugsProgram = simpleProgram "ffihugs"

529
happyProgram :: Program
530
531
532
533
534
535
536
537
happyProgram = (simpleProgram "happy") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      -- Invoking "happy --version" gives a string like
      -- "Happy Version 1.16 Copyright (c) ...."
      case words str of
        (_:_:ver:_) -> ver
        _           -> ""
  }
538

ijones's avatar
ijones committed
539
alexProgram :: Program
540
541
542
543
544
545
546
547
548
alexProgram = (simpleProgram "alex") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      -- Invoking "alex --version" gives a string like
      -- "Alex version 2.1.0, (c) 2003 Chris Dornan and Simon Marlow"
      case words str of
        (_:_:ver:_) -> takeWhile (`elem` ('.':['0'..'9'])) ver
        _           -> ""
  }

ijones's avatar
ijones committed
549

550
551
552
553
554
555
ranlibProgram :: Program
ranlibProgram = simpleProgram "ranlib"

arProgram :: Program
arProgram = simpleProgram "ar"

ijones's avatar
ijones committed
556
hsc2hsProgram :: Program
557
hsc2hsProgram = (simpleProgram "hsc2hs") {
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
    programFindVersion = \verbosity path -> do
      maybeVersion <- findProgramVersion "--version" (\str ->
        -- Invoking "hsc2hs --version" gives a string like "hsc2hs version 0.66"
        case words str of
          (_:_:ver:_) -> ver
          _           -> "") verbosity path

      -- It turns out that it's important to know if hsc2hs is using gcc or ghc
      -- as it's C compiler since this affects how we escape C options.
      -- So here's a cunning hack, we make a temp .hsc file and call:
      -- hsch2s tmp.hsc --cflag=--version
      -- which passes --version through to ghc/gcc and we look at the result
      -- to see if it was indeed ghc or not.
      case maybeVersion of
        Nothing -> return Nothing
	Just version ->
574
          withTempFile "dist" "hsc" $ \hsc -> do
575
576
	    writeFile hsc ""
	    (str, _) <- rawSystemStdout' verbosity path [hsc, "--cflag=--version"]
577
	    try $ removeFile (dropExtension hsc ++ "_hsc_make.c")
578
579
580
581
582
	    case words str of
	      (_:"Glorious":"Glasgow":"Haskell":_)
	        -> return $ Just version { versionTags = ["ghc"] }
	      _ -> return $ Just version

583
  }
ijones's avatar
ijones committed
584
585

c2hsProgram :: Program
586
587
588
c2hsProgram = (simpleProgram "c2hs") {
    programFindVersion = findProgramVersion "--numeric-version" id
  }
ijones's avatar
ijones committed
589
590

cpphsProgram :: Program
591
592
593
594
595
596
597
cpphsProgram = (simpleProgram "cpphs") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      -- Invoking "cpphs --version" gives a string like "cpphs 1.3"
      case words str of
        (_:ver:_) -> ver
        _         -> ""
  }
ijones's avatar
ijones committed
598

Roberto Zunino's avatar
Roberto Zunino committed
599
hscolourProgram :: Program
600
hscolourProgram = (simpleProgram "hscolour") {
601
    programFindLocation = findProgramOnPath "HsColour",
602
603
604
605
606
607
    programFindVersion  = findProgramVersion "-version" $ \str ->
      -- Invoking "HsColour -version" gives a string like "HsColour 1.7"
      case words str of
        (_:ver:_) -> ver
        _         -> ""
  }
Roberto Zunino's avatar
Roberto Zunino committed
608

ijones's avatar
ijones committed
609
haddockProgram :: Program
610
611
612
613
614
615
616
617
haddockProgram = (simpleProgram "haddock") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      -- Invoking "haddock --version" gives a string like
      -- "Haddock version 0.8, (c) Simon Marlow 2006"
      case words str of
        (_:_:ver:_) -> takeWhile (`elem` ('.':['0'..'9'])) ver
        _           -> ""
  }
ijones's avatar
ijones committed
618
619
620
621
622

greencardProgram :: Program
greencardProgram = simpleProgram "greencard"

ldProgram :: Program
623
ldProgram = simpleProgram "ld"
624
625
626

tarProgram :: Program
tarProgram = simpleProgram "tar"
ijones's avatar
ijones committed
627
628
629
630
631
632

cppProgram :: Program
cppProgram = simpleProgram "cpp"

pfesetupProgram :: Program
pfesetupProgram = simpleProgram "pfesetup"
633
634
635
636
637

pkgConfigProgram :: Program
pkgConfigProgram = (simpleProgram "pkg-config") {
    programFindVersion = findProgramVersion "--version" id
  }