packages feed

KiCS-0.8.9: src/lib/Curry/Module/Distribution.curry

--------------------------------------------------------------------------------
--- This module contains functions to obtain information concerning the current
--- distribution of the Curry implementation.
--- Most of the information is based on the external constants 
--- <b>curryCompiler...</b>.
---
--- @author Bernd Brassel, Michael Hanus
--- @version June 2009
--------------------------------------------------------------------------------

module Distribution (
  curryCompiler,curryCompilerMajorVersion,curryCompilerMinorVersion,
  curryRuntime,curryRuntimeMajorVersion,curryRuntimeMinorVersion,
  currySubdir,inCurrySubdir,addCurrySubdir,
  
  rcFileName,rcFileContents,getRcVar,getRcVars,

  findFileInLoadPath,lookupFileInLoadPath,
  readFirstFileInLoadPath,getLoadPath,getLoadPathForFile,

  FrontendTarget(..), 
  
  FrontendParams, defaultParams,
  quiet, fullPath, outfile, logfile,
  setQuiet, setFullPath, setOutfile, setLogfile,

  callFrontend,callFrontendWithParams
  ) where

import List(intersperse)
import Char(toLower)
import System
import IO
import Directory
import FileGoodies
import PropertyFile

-----------------------------------------------------------------
-- Compiler and run-time environment name and version
-----------------------------------------------------------------

-- if you do not use other functions but 
-- if-then-else, and the _Prelude_ functions
-- (<), (>), (<=), (>=), (==)
-- directly on the following constants, 
-- the compiler might be able to eliminate
-- them at compile time.

--- The name of the Curry compiler (e.g., "pakcs" or "kics").
curryCompiler :: String
curryCompiler external

--- The major version number of the Curry compiler.
curryCompilerMajorVersion :: Int
curryCompilerMajorVersion external

--- The minor version number of the Curry compiler.
curryCompilerMinorVersion :: Int
curryCompilerMinorVersion external

--- The name of the run-time environment (e.g., "sicstus", "swi", or "ghc")
curryRuntime :: String
curryRuntime external

--- The major version number of the Curry run-time environment.
curryRuntimeMajorVersion :: Int
curryRuntimeMajorVersion external

--- The minor version number of the Curry run-time environment.
curryRuntimeMinorVersion :: Int
curryRuntimeMinorVersion external

---------------------------------------------------
-- retrieving user specified options from rc file
---------------------------------------------------

--- The name of the file specifying configuration parameters of the
--- current distribution. This file must have the usual format of
--- property files (see description in module PropertyFile).
rcFileName :: IO String
rcFileName = getEnviron "HOME" >>= return . (++"/."++curryCompiler++"rc")

--- Returns the current configuration parameters of the distribution.
--- This action yields the list of pairs (var,val).
rcFileContents :: IO [(String,String)]
rcFileContents = rcFileName >>= readPropertyFile

--- Look up a specific configuration variable as specified by user in his rc file.
--- Uppercase/lowercase is ignored for the variable names.
getRcVar :: String -> IO (Maybe String)
getRcVar var = getRcVars [var] >>= return . head

--- Look up configuration variables as specified by user in his rc file.
--- Uppercase/lowercase is ignored for the variable names.
getRcVars :: [String] -> IO [Maybe String]
getRcVars vars = do
  rcs <- rcFileContents 
  return (map (flip lookup (map (\ (a,b)->(map toLower a,b)) rcs))
              (map (map toLower) vars))

-----------------------------------------------------------
--- finding files in correspondence to compiler load path
-----------------------------------------------------------

-- finding the standard libraries
getStdLibDir :: IO String
getStdLibDir external

-- finding the frontend binary
getFrontendCall :: IO String
getFrontendCall external

--- Name of the main installation directory of the Curry compiler.
installDir :: String
installDir external

--- Name of the sub directory where auxiliary files (.fint, .fcy, etc)
--- are stored.
currySubdir :: String
currySubdir = ".curry"

--- Transforms a file name by adding the currySubDir to the file name.
inCurrySubdir :: String -> String
inCurrySubdir filename =
  let (base,file) = splitDirectoryBaseName filename
   in base++'/':currySubdir++'/':file

--- Transforms a directory name into the name of the corresponding
--- sub directory containing auxiliary files.
addCurrySubdir :: String -> String
addCurrySubdir dir = dir++'/':currySubdir

--- Returns the current path (list of directory names) of the
--- system libraries.
getSysLibPath :: IO [String]
getSysLibPath = 
  if curryCompiler == "pakcs" 
    then do
       pakcspath <- getEnviron "PAKCSLIBPATH"
       return $
          (if null pakcspath
               then [installDir++"/lib",installDir++"/lib/meta"]
               else splitPath pakcspath)
    else if curryCompiler == "kics" then getStdLibDir >>= return . (:[])
    else error "Distribution.getSysLibPath: unknown curryCompiler"


--- Adds a directory name to a file by looking up the current load path.
--- An error message is delivered if there is no such file.
lookupFileInLoadPath :: String -> IO (Maybe String)
lookupFileInLoadPath fn = 
  getLoadPathForFile fn >>= lookupFileInPath (baseName fn) [""] 

--- Adds a directory name to a file by looking up the current load path.
--- An error message is delivered if there is no such file.
findFileInLoadPath :: String -> IO String
findFileInLoadPath fn = 
  getLoadPathForFile fn >>= getFileInPath (baseName fn) [""] 

--- Returns the contents of the file found first in the current load path.
--- An error message is delivered if there is no such file.
readFirstFileInLoadPath :: String -> IO String
readFirstFileInLoadPath fn = findFileInLoadPath fn >>= readFile 

--- Returns the current path (list of directory names) that is
--- used for loading modules.
getLoadPath :: IO [String]
getLoadPath = getLoadPathForFile "xxx"

--- Returns the current path (list of directory names) that is
--- used for loading modules w.r.t. a given main module file.
--- The directory prefix of the module file (or "." if there is
--- no such prefix) is the first element of the load path and the
--- remaining elements are determined by the environment variables
--- CURRYRPATH and PAKCSLIBPATH when using pakcs and
--- the entry of kicsrc when using kics, respectively.
getLoadPathForFile :: String -> IO [String]
getLoadPathForFile file = do
  syslib <- getSysLibPath
  mblib <- getRcVar "Libraries"
  let fileDir = dirName file 
  if curryCompiler == "pakcs" || curryCompiler == "kics" then
    do currypath <- getEnviron "CURRYPATH"
       let llib = maybe [] splitPath mblib
       return (addCurrySubdirs (fileDir :
                                   (if null currypath
                                    then []
                                    else splitPath currypath) ++
                                    llib ++ syslib))

    else error "Distribution.getLoadPathForFile: unknown curryCompiler"
 where
  addCurrySubdirs = concatMap (\d->[d,addCurrySubdir d])


-------------------------------------------------------------------
-- calling the front end
-------------------------------------------------------------------

--- Data type for representing the different target files that can be produced
--- by the front end of the Curry compiler.
--- @cons FCY - FlatCurry file ending with .fcy
--- @cons FINT - FlatCurry interface file ending with .fint
--- @cons ACY - AbstractCurry file ending with .acy
--- @cons UACY - Untyped (without type checking) AbstractCurry file ending with .uacy
--- @cons HTML - colored HTML representation of source program
--- @cons CY - source representation employed by the frontend
data FrontendTarget = FCY | FINT | ACY | UACY | HTML | CY

--- Abstract data type for representing parameters supported by the front end
--- of the Curry compiler.
-- The parameters are of the form
-- FrontendParams Quiet FullPath OutFile LogFile
-- where
--   Quiet - work silently
--   FullPath dirs - the complete list of directory names for loading modules
--   OutFile file - output file (currently, only relevant for HTML target)
--   LogFile file - store all output (including errors) of the front end in file
data FrontendParams =
  FrontendParams Bool (Maybe [String]) (Maybe String) (Maybe String)

--- The default parameters of the front end.
defaultParams :: FrontendParams
defaultParams = FrontendParams False Nothing Nothing Nothing

--- Set quiet mode of the front end.
setQuiet :: Bool -> FrontendParams -> FrontendParams
setQuiet s (FrontendParams _ x y z) = FrontendParams s x y z

--- Set the full path of the front end.
--- If this parameter is set, the front end searches all modules
--- in this path (instead of using the default path).
setFullPath ::  [String] -> FrontendParams -> FrontendParams 
setFullPath s (FrontendParams x _ y z) = FrontendParams x (Just s) y z

--- Set the outfile parameter of the front end.
--- Relevant for HTML generation.
setOutfile ::  String -> FrontendParams -> FrontendParams 
setOutfile  s (FrontendParams x y _ z) = FrontendParams x y (Just s) z

--- Set the logfile parameter of the front end.
--- If this parameter is set, all messages produced by the front end
--- are stored in this file.
setLogfile ::  String -> FrontendParams -> FrontendParams 
setLogfile  s (FrontendParams x y z _) = FrontendParams x y z (Just s)

--- Returns the value of the "quiet" parameter.
quiet :: FrontendParams -> Bool
quiet (FrontendParams x _ _ _) = x

--- Returns the full path parameter of the front end.
fullPath :: FrontendParams -> Maybe [String]
fullPath (FrontendParams _ x _ _) = x

--- Returns the outfile parameter of the front end.
outfile :: FrontendParams -> Maybe String
outfile  (FrontendParams _ _ x _) = x

--- Returns the logfile parameter of the front end.
logfile :: FrontendParams -> Maybe String
logfile  (FrontendParams _ _ _ x) = x

--- In order to make sure that compiler generated files (like .fcy, .fint, .acy)
--- are up to date, one can call the front end of the Curry compiler with this action.
--- @param target - the kind of target file to be generated
--- @param progname - the name of the main module of the application to be compiled
callFrontend :: FrontendTarget -> String -> IO ()
callFrontend target = callFrontendWithParams target defaultParams

--- In order to make sure that compiler generated files (like .fcy, .fint, .acy)
--- are up to date, one can call the front end of the Curry compiler
--- with this action where various parameters can be set.
--- @param target - the kind of target file to be generated
--- @param params - parameters for the front end
--- @param progname - the name of the main module of the application to be compiled
callFrontendWithParams :: FrontendTarget -> FrontendParams -> String -> IO ()
callFrontendWithParams target params progname = do
  parsecurry <- callParseCurry
  let lf      = maybe "" id (logfile params)
      syscall = parsecurry ++ " " ++ showFrontendTarget target
                     ++ showFrontendParams 
                     ++ " " ++ progname
  print syscall
  if null lf
    then system syscall
    else system (syscall++" > "++lf++" 2>&1")
  return ()
 where
   isPakcs =
     if curryCompiler == "pakcs" then True else
     if curryCompiler == "kics"  then False
       else error "Distribution.callFrontend: unknown curryCompiler"

   callParseCurry =
     if isPakcs then
      do return (installDir++"/bin/parsecurry")
     else
      do path <- maybe getLoadPath return (fullPath params)
         frontend <- getFrontendCall
         let call = frontend++concatMap (" -i"++) path
         print call
         return call

   showFrontendTarget FCY  = "--flat"
   showFrontendTarget FINT = "--flat"
   showFrontendTarget ACY  = "--acy"
   showFrontendTarget UACY = "--uacy"
   showFrontendTarget HTML = "--html"
   showFrontendTarget CY   = "--parse-only"

   showFrontendParams =
     (if quiet params then runQuiet else "")
     ++ (maybe "" (" -o "++) (outfile params))
     ++ (maybe "" (\p -> " --fullpath " ++ concat (intersperse ":" p))
               (fullPath params))

   runQuiet = if isPakcs then " --quiet "
                         else " --no-verb --no-warn --no-overlap-warn " -- kics

rcErr :: String -> a -> IO a
rcErr s x = hPutStrLn stderr (s ++ " undefined in rc file") >> return x