Program.hs 23.8 KB
Newer Older
ijones's avatar
ijones committed
1
2
-----------------------------------------------------------------------------
-- |
3
-- Module      :  Distribution.Simple.Program
ijones's avatar
ijones committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 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.

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

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

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

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

    -- * 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
83
    , pkgConfigProgram
84
    ) where
ijones's avatar
ijones committed
85

86
import qualified Data.Map as Map
87
import Distribution.Simple.Utils (die, debug, warn, rawSystemExit,
88
89
                                  rawSystemStdout, rawSystemStdout',
                                  withTempFile)
90
import Distribution.Version (Version(..), readVersion, showVersion,
91
                             VersionRange(..), withinRange, showVersionRange)
92
import Distribution.Verbosity
93
import System.Directory (doesFileExist, removeFile, findExecutable)
94
import System.FilePath  (dropExtension)
95
import System.IO (hClose)
96
import System.IO.Error (try)
97
import Control.Monad (join, foldM)
98
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
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
133
-- not.  This includes not just the path, but the program as well.
134
135
data ProgramLocation
    = UserSpecified { locationPath :: FilePath }
nominolo@gmail.com's avatar
nominolo@gmail.com committed
136
137
      -- ^The user gave the path to this program,
      -- eg. --ghc-path=\/usr\/bin\/ghc-6.6
138
    | FoundOnSystem { locationPath :: FilePath }
nominolo@gmail.com's avatar
nominolo@gmail.com committed
139
      -- ^The location of the program, as located by searching PATH.
ijones's avatar
ijones committed
140
      deriving (Read, Show)
141

142
143
144
-- ------------------------------------------------------------
-- * Programs functions
-- ------------------------------------------------------------
145

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

150
151
152
153
154
155
156
157
158
-- | 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 = 
159
  Program name (findProgramOnPath name) (\_ _ -> return Nothing)
160
161

-- | Look for a program on the path.
162
163
findProgramOnPath :: FilePath -> Verbosity -> IO (Maybe FilePath)
findProgramOnPath prog verbosity = do
164
  debug verbosity $ "searching for " ++ prog ++ " in path."
165
  res <- findExecutable prog
166
167
168
  case res of
      Nothing   -> debug verbosity ("Cannot find " ++ prog ++ " on the path")
      Just path -> debug verbosity ("found " ++ prog ++ " at "++ path)
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  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
186
187
188
      Nothing -> warn verbosity $ "cannot determine version of " ++ path
                               ++ " :\n" ++ show str
      Just v  -> debug verbosity $ path ++ " is version " ++ showVersion v
189
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
  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
Ian Lynagh's avatar
Ian Lynagh committed
221
222
updateUnconfiguredProgs update conf =
  conf { unconfiguredProgs = update (unconfiguredProgs conf) }
223
224
updateConfiguredProgs :: (ConfiguredProgs -> ConfiguredProgs)
                      -> ProgramConfiguration -> ProgramConfiguration
Ian Lynagh's avatar
Ian Lynagh committed
225
226
updateConfiguredProgs update conf =
  conf { configuredProgs = update (configuredProgs conf) }
227
228
229
230
231

-- 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.
232
instance Show ProgramConfiguration where
233
  show = show . Map.toAscList . configuredProgs
234
235

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

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

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

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

252
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
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
278
279
280
281
282
283
284
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' })
285
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

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.
314
--
315
316
317
-- 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.
318
--
319
320
321
322
323
324
325
326
327
328
329
330
331
332
-- 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
333
334
335
    Just path -> do
      absolute <- doesFileExist path
      if absolute
336
        then return (Just (UserSpecified path))
337
        else findProgramOnPath path verbosity
338
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
         >>= 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
400

401
402
403
-- ------------------------------------------------------------
-- * Running programs
-- ------------------------------------------------------------
404

405
406
407
408
409
410
411
412
-- | 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)

413
414
415
416
417
418
419
420
-- | 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)

421
422
423
424
425
426
427
428
-- | 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
429
    Nothing -> die ("The program " ++ programName prog ++ " is required but it could not be found")
430
    Just configuredProg -> rawSystemProgram verbosity configuredProg extraArgs
431

432
433
434
435
436
437
438
439
-- | 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
440
    Nothing -> die ("The program " ++ programName prog ++ " is required but it could not be found")
441
442
    Just configuredProg -> rawSystemProgramStdout verbosity configuredProg extraArgs

ijones's avatar
ijones committed
443
-- ------------------------------------------------------------
444
-- * Known programs
ijones's avatar
ijones committed
445
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
-- | 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
475
476
    -- configuration tools
    , pkgConfigProgram
477
478
    ]

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

ghcPkgProgram :: Program
485
486
487
488
489
490
491
492
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
493
494

nhcProgram :: Program
495
496
497
nhcProgram = (simpleProgram "nhc98") {
    programFindVersion = findProgramVersion "--version" $ \str ->
      -- Invoking "nhc98 --version" gives a string like
Duncan Coutts's avatar
Duncan Coutts committed
498
      -- "/usr/local/bin/nhc98: v1.20 (2007-11-22)"
499
500
501
502
      case words str of
        (_:('v':ver):_) -> ver
        _               -> ""
  }
ijones's avatar
ijones committed
503

504
505
506
hmakeProgram :: Program
hmakeProgram = (simpleProgram "hmake") {
    programFindVersion = findProgramVersion "--version" $ \str ->
507
508
    -- Invoking "hmake --version" gives a string line
    -- "/usr/local/bin/hmake: 3.13 (2006-11-01)"
509
510
511
512
      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
575
          withTempFile "dist" ".hsc" $ \hsc hnd -> do
	    hClose hnd
576
	    (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
  }