1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
------------------------------------------------------------------------------
--- Some auxiliary operations to deal with executables (system commands)
--- used by CPM.
------------------------------------------------------------------------------

module CPM.Executables
  ( checkRequiredExecutables, getCurlCmd, getCurlCmdOpts
  , getCurryCheck, getCurryDoc )
 where

import Control.Monad       ( unless )
import Data.List           ( intercalate )

import System.Directory    ( doesFileExist )
import System.FilePath     ( (</>) )
import System.Path         ( fileInPath, getFileInPath )
import System.Process      ( exitWith )

import CPM.Config          ( Config, binInstallDir )
import CPM.ErrorLogger

------------------------------------------------------------------------------
--- Check whether all operating system executables required by CPM are present
--- on the current system.
--- Since this takes some time, it is only checked with CPM's `update` command.
checkRequiredExecutables :: ErrorLogger ()
checkRequiredExecutables = do
  logDebug "Checking whether all required executables can be found..."
  missingExecutables <- liftIOEL $ checkExecutables listOfExecutables
  unless (null missingExecutables) $ do
    logError $ "The following programs could not be found on the PATH " ++
                   "(they are required for CPM to work):\n" ++
                   intercalate ", " missingExecutables
    liftIOEL $ exitWith 1
  logDebug "All required executables found."
 where
  listOfExecutables =
    [ "curl"
    , "git"
    , "unzip"
    , "tar"
    , "cp"
    , "rm"
    , "ln"
    , "readlink"
    , "realpath" ]

--- Filters from a given list of executable names the executables
--- that cannot be found in one of the directories of the environment
--- variable `PATH`. 
checkExecutables :: [String] -> IO [String]
checkExecutables executables = do
  present <- mapM fileInPath executables
  return $ map fst $ filter (not . snd) (zip executables present)

------------------------------------------------------------------------------
--- Returns the `curl` command (first component of the result)
--- together with some standard options. If the log level is not `Debug`,
--- the options `--silent --show-error` are added so that
--- `curl` works in silent mode. Moreover, a max-time is used to avoid
--- hanging forever if a server cannot be reached.
getCurlCmdOpts :: ErrorLogger (String,[String])
getCurlCmdOpts = do
  ll <- getLogLevel
  return $ ("curl",
            ["--max-time", "30"] ++
            (if ll == Debug then [] else ["--silent", "--show-error"]))

--- Returns the `curl` command with some standard options as a string.
--- If the log level is not `Debug`, the options `--silent --show-error`
--- are added so that `curl` works in silent mode. Moreover, a max-time
--- is used to avoid hanging forever if a server cannot be reached.
getCurlCmd :: ErrorLogger String
getCurlCmd = fmap (\(c,os) -> unwords (c:os)) getCurlCmdOpts

------------------------------------------------------------------------------
--- Returns the `curry-check` command, either from the current path
--- or from CPM's bin directory, or `Nothing` if it does not exist.
--- If it does not exist, report this also as an info.
getCurryCheck :: Config -> ErrorLogger (Maybe String)
getCurryCheck cfg = do
  mbf <- liftIOEL $ getFileInPath ccbin
  maybe (do let cpmcurrycheck = binInstallDir cfg </> ccbin
            ccex <- liftIOEL $ doesFileExist cpmcurrycheck
            if ccex then return $ Just cpmcurrycheck
                    else do logInfo "Executable 'curry-check' not found!"
                            return Nothing
        )
        (return . Just)
        mbf
 where
  ccbin = "curry-check"

------------------------------------------------------------------------------
--- Returns the `curry-doc` command, either from the current path
--- or from CPM's bin directory. Fails with an error if it does not exist.
getCurryDoc :: Config -> ErrorLogger String
getCurryDoc cfg = do
  mbf <- liftIOEL $ getFileInPath cdbin
  maybe (do let cpmcurrydoc = binInstallDir cfg </> cdbin
            cdex <- liftIOEL $ doesFileExist cpmcurrydoc
            if cdex then return cpmcurrydoc
                    else fail $ "Executable '" ++ cdbin ++ "' not found!"
        )
        return
        mbf
 where
  cdbin = "curry-doc"

------------------------------------------------------------------------------