621 lines
27 KiB
Haskell
621 lines
27 KiB
Haskell
|
-----------------------------------------------------------------------------
|
||
|
-- |
|
||
|
-- Module : Distribution.Simple.PreProcess
|
||
|
-- Copyright : (c) 2003-2005, Isaac Jones, Malcolm Wallace
|
||
|
-- License : BSD3
|
||
|
--
|
||
|
-- Maintainer : cabal-devel@haskell.org
|
||
|
-- Portability : portable
|
||
|
--
|
||
|
-- This defines a 'PreProcessor' abstraction which represents a pre-processor
|
||
|
-- that can transform one kind of file into another. There is also a
|
||
|
-- 'PPSuffixHandler' which is a combination of a file extension and a function
|
||
|
-- for configuring a 'PreProcessor'. It defines a bunch of known built-in
|
||
|
-- preprocessors like @cpp@, @cpphs@, @c2hs@, @hsc2hs@, @happy@, @alex@ etc and
|
||
|
-- lists them in 'knownSuffixHandlers'. On top of this it provides a function
|
||
|
-- for actually preprocessing some sources given a bunch of known suffix
|
||
|
-- handlers. This module is not as good as it could be, it could really do with
|
||
|
-- a rewrite to address some of the problems we have with pre-processors.
|
||
|
|
||
|
module Distribution.Simple.PreProcess (preprocessComponent, knownSuffixHandlers,
|
||
|
ppSuffixes, PPSuffixHandler, PreProcessor(..),
|
||
|
mkSimplePreProcessor, runSimplePreProcessor,
|
||
|
ppCpp, ppCpp', ppGreenCard, ppC2hs, ppHsc2hs,
|
||
|
ppHappy, ppAlex, ppUnlit, platformDefines
|
||
|
)
|
||
|
where
|
||
|
|
||
|
|
||
|
import Control.Monad
|
||
|
import Distribution.Simple.PreProcess.Unlit (unlit)
|
||
|
import Distribution.Package
|
||
|
( Package(..), PackageName(..) )
|
||
|
import qualified Distribution.ModuleName as ModuleName
|
||
|
import Distribution.PackageDescription as PD
|
||
|
( PackageDescription(..), BuildInfo(..)
|
||
|
, Executable(..)
|
||
|
, Library(..), libModules
|
||
|
, TestSuite(..), testModules
|
||
|
, TestSuiteInterface(..)
|
||
|
, Benchmark(..), benchmarkModules, BenchmarkInterface(..) )
|
||
|
import qualified Distribution.InstalledPackageInfo as Installed
|
||
|
( InstalledPackageInfo_(..) )
|
||
|
import qualified Distribution.Simple.PackageIndex as PackageIndex
|
||
|
import Distribution.Simple.CCompiler
|
||
|
( cSourceExtensions )
|
||
|
import Distribution.Simple.Compiler
|
||
|
( CompilerFlavor(..)
|
||
|
, compilerFlavor, compilerCompatVersion, compilerVersion )
|
||
|
import Distribution.Simple.LocalBuildInfo
|
||
|
( LocalBuildInfo(..), Component(..) )
|
||
|
import Distribution.Simple.BuildPaths (autogenModulesDir,cppHeaderName)
|
||
|
import Distribution.Simple.Utils
|
||
|
( createDirectoryIfMissingVerbose, withUTF8FileContents, writeUTF8File
|
||
|
, die, setupMessage, intercalate, copyFileVerbose, moreRecentFile
|
||
|
, findFileWithExtension, findFileWithExtension' )
|
||
|
import Distribution.Simple.Program
|
||
|
( Program(..), ConfiguredProgram(..), programPath
|
||
|
, requireProgram, requireProgramVersion
|
||
|
, rawSystemProgramConf, rawSystemProgram
|
||
|
, greencardProgram, cpphsProgram, hsc2hsProgram, c2hsProgram
|
||
|
, happyProgram, alexProgram, ghcProgram, ghcjsProgram, gccProgram )
|
||
|
import Distribution.Simple.Test.LibV09
|
||
|
( writeSimpleTestStub, stubFilePath, stubName )
|
||
|
import Distribution.System
|
||
|
( OS(..), buildOS, Arch(..), Platform(..) )
|
||
|
import Distribution.Text
|
||
|
import Distribution.Version
|
||
|
( Version(..), anyVersion, orLaterVersion )
|
||
|
import Distribution.Verbosity
|
||
|
|
||
|
import Data.Maybe (fromMaybe)
|
||
|
import Data.List (nub)
|
||
|
import System.Directory (doesFileExist)
|
||
|
import System.Info (os, arch)
|
||
|
import System.FilePath (splitExtension, dropExtensions, (</>), (<.>),
|
||
|
takeDirectory, normalise, replaceExtension)
|
||
|
|
||
|
-- |The interface to a preprocessor, which may be implemented using an
|
||
|
-- external program, but need not be. The arguments are the name of
|
||
|
-- the input file, the name of the output file and a verbosity level.
|
||
|
-- Here is a simple example that merely prepends a comment to the given
|
||
|
-- source file:
|
||
|
--
|
||
|
-- > ppTestHandler :: PreProcessor
|
||
|
-- > ppTestHandler =
|
||
|
-- > PreProcessor {
|
||
|
-- > platformIndependent = True,
|
||
|
-- > runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->
|
||
|
-- > do info verbosity (inFile++" has been preprocessed to "++outFile)
|
||
|
-- > stuff <- readFile inFile
|
||
|
-- > writeFile outFile ("-- preprocessed as a test\n\n" ++ stuff)
|
||
|
-- > return ExitSuccess
|
||
|
--
|
||
|
-- We split the input and output file names into a base directory and the
|
||
|
-- rest of the file name. The input base dir is the path in the list of search
|
||
|
-- dirs that this file was found in. The output base dir is the build dir where
|
||
|
-- all the generated source files are put.
|
||
|
--
|
||
|
-- The reason for splitting it up this way is that some pre-processors don't
|
||
|
-- simply generate one output .hs file from one input file but have
|
||
|
-- dependencies on other generated files (notably c2hs, where building one
|
||
|
-- .hs file may require reading other .chi files, and then compiling the .hs
|
||
|
-- file may require reading a generated .h file). In these cases the generated
|
||
|
-- files need to embed relative path names to each other (eg the generated .hs
|
||
|
-- file mentions the .h file in the FFI imports). This path must be relative to
|
||
|
-- the base directory where the generated files are located, it cannot be
|
||
|
-- relative to the top level of the build tree because the compilers do not
|
||
|
-- look for .h files relative to there, ie we do not use \"-I .\", instead we
|
||
|
-- use \"-I dist\/build\" (or whatever dist dir has been set by the user)
|
||
|
--
|
||
|
-- Most pre-processors do not care of course, so mkSimplePreProcessor and
|
||
|
-- runSimplePreProcessor functions handle the simple case.
|
||
|
--
|
||
|
data PreProcessor = PreProcessor {
|
||
|
|
||
|
-- Is the output of the pre-processor platform independent? eg happy output
|
||
|
-- is portable haskell but c2hs's output is platform dependent.
|
||
|
-- This matters since only platform independent generated code can be
|
||
|
-- inlcuded into a source tarball.
|
||
|
platformIndependent :: Bool,
|
||
|
|
||
|
-- TODO: deal with pre-processors that have implementaion dependent output
|
||
|
-- eg alex and happy have --ghc flags. However we can't really inlcude
|
||
|
-- ghc-specific code into supposedly portable source tarballs.
|
||
|
|
||
|
runPreProcessor :: (FilePath, FilePath) -- Location of the source file relative to a base dir
|
||
|
-> (FilePath, FilePath) -- Output file name, relative to an output base dir
|
||
|
-> Verbosity -- verbosity
|
||
|
-> IO () -- Should exit if the preprocessor fails
|
||
|
}
|
||
|
|
||
|
mkSimplePreProcessor :: (FilePath -> FilePath -> Verbosity -> IO ())
|
||
|
-> (FilePath, FilePath)
|
||
|
-> (FilePath, FilePath) -> Verbosity -> IO ()
|
||
|
mkSimplePreProcessor simplePP
|
||
|
(inBaseDir, inRelativeFile)
|
||
|
(outBaseDir, outRelativeFile) verbosity = simplePP inFile outFile verbosity
|
||
|
where inFile = normalise (inBaseDir </> inRelativeFile)
|
||
|
outFile = normalise (outBaseDir </> outRelativeFile)
|
||
|
|
||
|
runSimplePreProcessor :: PreProcessor -> FilePath -> FilePath -> Verbosity
|
||
|
-> IO ()
|
||
|
runSimplePreProcessor pp inFile outFile verbosity =
|
||
|
runPreProcessor pp (".", inFile) (".", outFile) verbosity
|
||
|
|
||
|
-- |A preprocessor for turning non-Haskell files with the given extension
|
||
|
-- into plain Haskell source files.
|
||
|
type PPSuffixHandler
|
||
|
= (String, BuildInfo -> LocalBuildInfo -> PreProcessor)
|
||
|
|
||
|
-- | Apply preprocessors to the sources from 'hsSourceDirs' for a given
|
||
|
-- component (lib, exe, or test suite).
|
||
|
preprocessComponent :: PackageDescription
|
||
|
-> Component
|
||
|
-> LocalBuildInfo
|
||
|
-> Bool
|
||
|
-> Verbosity
|
||
|
-> [PPSuffixHandler]
|
||
|
-> IO ()
|
||
|
preprocessComponent pd comp lbi isSrcDist verbosity handlers = case comp of
|
||
|
(CLib lib@Library{ libBuildInfo = bi }) -> do
|
||
|
let dirs = hsSourceDirs bi ++ [autogenModulesDir lbi]
|
||
|
setupMessage verbosity "Preprocessing library" (packageId pd)
|
||
|
forM_ (map ModuleName.toFilePath $ libModules lib) $
|
||
|
pre dirs (buildDir lbi) (localHandlers bi)
|
||
|
(CExe exe@Executable { buildInfo = bi, exeName = nm }) -> do
|
||
|
let exeDir = buildDir lbi </> nm </> nm ++ "-tmp"
|
||
|
dirs = hsSourceDirs bi ++ [autogenModulesDir lbi]
|
||
|
setupMessage verbosity ("Preprocessing executable '" ++ nm ++ "' for") (packageId pd)
|
||
|
forM_ (map ModuleName.toFilePath $ otherModules bi) $
|
||
|
pre dirs exeDir (localHandlers bi)
|
||
|
pre (hsSourceDirs bi) exeDir (localHandlers bi) $
|
||
|
dropExtensions (modulePath exe)
|
||
|
CTest test@TestSuite{ testName = nm } -> do
|
||
|
setupMessage verbosity ("Preprocessing test suite '" ++ nm ++ "' for") (packageId pd)
|
||
|
case testInterface test of
|
||
|
TestSuiteExeV10 _ f ->
|
||
|
preProcessTest test f $ buildDir lbi </> testName test
|
||
|
</> testName test ++ "-tmp"
|
||
|
TestSuiteLibV09 _ _ -> do
|
||
|
let testDir = buildDir lbi </> stubName test
|
||
|
</> stubName test ++ "-tmp"
|
||
|
writeSimpleTestStub test testDir
|
||
|
preProcessTest test (stubFilePath test) testDir
|
||
|
TestSuiteUnsupported tt -> die $ "No support for preprocessing test "
|
||
|
++ "suite type " ++ display tt
|
||
|
CBench bm@Benchmark{ benchmarkName = nm } -> do
|
||
|
setupMessage verbosity ("Preprocessing benchmark '" ++ nm ++ "' for") (packageId pd)
|
||
|
case benchmarkInterface bm of
|
||
|
BenchmarkExeV10 _ f ->
|
||
|
preProcessBench bm f $ buildDir lbi </> benchmarkName bm
|
||
|
</> benchmarkName bm ++ "-tmp"
|
||
|
BenchmarkUnsupported tt -> die $ "No support for preprocessing benchmark "
|
||
|
++ "type " ++ display tt
|
||
|
where
|
||
|
builtinHaskellSuffixes = ["hs", "lhs", "hsig", "lhsig"]
|
||
|
builtinCSuffixes = cSourceExtensions
|
||
|
builtinSuffixes = builtinHaskellSuffixes ++ builtinCSuffixes
|
||
|
localHandlers bi = [(ext, h bi lbi) | (ext, h) <- handlers]
|
||
|
pre dirs dir lhndlrs fp =
|
||
|
preprocessFile dirs dir isSrcDist fp verbosity builtinSuffixes lhndlrs
|
||
|
preProcessTest test = preProcessComponent (testBuildInfo test)
|
||
|
(testModules test)
|
||
|
preProcessBench bm = preProcessComponent (benchmarkBuildInfo bm)
|
||
|
(benchmarkModules bm)
|
||
|
preProcessComponent bi modules exePath dir = do
|
||
|
let biHandlers = localHandlers bi
|
||
|
sourceDirs = hsSourceDirs bi ++ [ autogenModulesDir lbi ]
|
||
|
sequence_ [ preprocessFile sourceDirs dir isSrcDist
|
||
|
(ModuleName.toFilePath modu) verbosity builtinSuffixes
|
||
|
biHandlers
|
||
|
| modu <- modules ]
|
||
|
preprocessFile (dir : (hsSourceDirs bi)) dir isSrcDist
|
||
|
(dropExtensions $ exePath) verbosity
|
||
|
builtinSuffixes biHandlers
|
||
|
|
||
|
--TODO: try to list all the modules that could not be found
|
||
|
-- not just the first one. It's annoying and slow due to the need
|
||
|
-- to reconfigure after editing the .cabal file each time.
|
||
|
|
||
|
-- |Find the first extension of the file that exists, and preprocess it
|
||
|
-- if required.
|
||
|
preprocessFile
|
||
|
:: [FilePath] -- ^source directories
|
||
|
-> FilePath -- ^build directory
|
||
|
-> Bool -- ^preprocess for sdist
|
||
|
-> FilePath -- ^module file name
|
||
|
-> Verbosity -- ^verbosity
|
||
|
-> [String] -- ^builtin suffixes
|
||
|
-> [(String, PreProcessor)] -- ^possible preprocessors
|
||
|
-> IO ()
|
||
|
preprocessFile searchLoc buildLoc forSDist baseFile verbosity builtinSuffixes handlers = do
|
||
|
-- look for files in the various source dirs with this module name
|
||
|
-- and a file extension of a known preprocessor
|
||
|
psrcFiles <- findFileWithExtension' (map fst handlers) searchLoc baseFile
|
||
|
case psrcFiles of
|
||
|
-- no preprocessor file exists, look for an ordinary source file
|
||
|
-- just to make sure one actually exists at all for this module.
|
||
|
-- Note: by looking in the target/output build dir too, we allow
|
||
|
-- source files to appear magically in the target build dir without
|
||
|
-- any corresponding "real" source file. This lets custom Setup.hs
|
||
|
-- files generate source modules directly into the build dir without
|
||
|
-- the rest of the build system being aware of it (somewhat dodgy)
|
||
|
Nothing -> do
|
||
|
bsrcFiles <- findFileWithExtension builtinSuffixes (buildLoc : searchLoc) baseFile
|
||
|
case bsrcFiles of
|
||
|
Nothing -> die $ "can't find source for " ++ baseFile
|
||
|
++ " in " ++ intercalate ", " searchLoc
|
||
|
_ -> return ()
|
||
|
-- found a pre-processable file in one of the source dirs
|
||
|
Just (psrcLoc, psrcRelFile) -> do
|
||
|
let (srcStem, ext) = splitExtension psrcRelFile
|
||
|
psrcFile = psrcLoc </> psrcRelFile
|
||
|
pp = fromMaybe (error "Internal error in preProcess module: Just expected")
|
||
|
(lookup (tailNotNull ext) handlers)
|
||
|
-- Preprocessing files for 'sdist' is different from preprocessing
|
||
|
-- for 'build'. When preprocessing for sdist we preprocess to
|
||
|
-- avoid that the user has to have the preprocessors available.
|
||
|
-- ATM, we don't have a way to specify which files are to be
|
||
|
-- preprocessed and which not, so for sdist we only process
|
||
|
-- platform independent files and put them into the 'buildLoc'
|
||
|
-- (which we assume is set to the temp. directory that will become
|
||
|
-- the tarball).
|
||
|
--TODO: eliminate sdist variant, just supply different handlers
|
||
|
when (not forSDist || forSDist && platformIndependent pp) $ do
|
||
|
-- look for existing pre-processed source file in the dest dir to
|
||
|
-- see if we really have to re-run the preprocessor.
|
||
|
ppsrcFiles <- findFileWithExtension builtinSuffixes [buildLoc] baseFile
|
||
|
recomp <- case ppsrcFiles of
|
||
|
Nothing -> return True
|
||
|
Just ppsrcFile ->
|
||
|
psrcFile `moreRecentFile` ppsrcFile
|
||
|
when recomp $ do
|
||
|
let destDir = buildLoc </> dirName srcStem
|
||
|
createDirectoryIfMissingVerbose verbosity True destDir
|
||
|
runPreProcessorWithHsBootHack pp
|
||
|
(psrcLoc, psrcRelFile)
|
||
|
(buildLoc, srcStem <.> "hs")
|
||
|
|
||
|
where
|
||
|
dirName = takeDirectory
|
||
|
tailNotNull [] = []
|
||
|
tailNotNull x = tail x
|
||
|
|
||
|
-- FIXME: This is a somewhat nasty hack. GHC requires that hs-boot files
|
||
|
-- be in the same place as the hs files, so if we put the hs file in dist/
|
||
|
-- then we need to copy the hs-boot file there too. This should probably be
|
||
|
-- done another way. Possibly we should also be looking for .lhs-boot
|
||
|
-- files, but I think that preprocessors only produce .hs files.
|
||
|
runPreProcessorWithHsBootHack pp
|
||
|
(inBaseDir, inRelativeFile)
|
||
|
(outBaseDir, outRelativeFile) = do
|
||
|
runPreProcessor pp
|
||
|
(inBaseDir, inRelativeFile)
|
||
|
(outBaseDir, outRelativeFile) verbosity
|
||
|
|
||
|
exists <- doesFileExist inBoot
|
||
|
when exists $ copyFileVerbose verbosity inBoot outBoot
|
||
|
|
||
|
where
|
||
|
inBoot = replaceExtension inFile "hs-boot"
|
||
|
outBoot = replaceExtension outFile "hs-boot"
|
||
|
|
||
|
inFile = normalise (inBaseDir </> inRelativeFile)
|
||
|
outFile = normalise (outBaseDir </> outRelativeFile)
|
||
|
|
||
|
-- ------------------------------------------------------------
|
||
|
-- * known preprocessors
|
||
|
-- ------------------------------------------------------------
|
||
|
|
||
|
ppGreenCard :: BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppGreenCard _ lbi
|
||
|
= PreProcessor {
|
||
|
platformIndependent = False,
|
||
|
runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->
|
||
|
rawSystemProgramConf verbosity greencardProgram (withPrograms lbi)
|
||
|
(["-tffi", "-o" ++ outFile, inFile])
|
||
|
}
|
||
|
|
||
|
-- This one is useful for preprocessors that can't handle literate source.
|
||
|
-- We also need a way to chain preprocessors.
|
||
|
ppUnlit :: PreProcessor
|
||
|
ppUnlit =
|
||
|
PreProcessor {
|
||
|
platformIndependent = True,
|
||
|
runPreProcessor = mkSimplePreProcessor $ \inFile outFile _verbosity ->
|
||
|
withUTF8FileContents inFile $ \contents ->
|
||
|
either (writeUTF8File outFile) die (unlit inFile contents)
|
||
|
}
|
||
|
|
||
|
ppCpp :: BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppCpp = ppCpp' []
|
||
|
|
||
|
ppCpp' :: [String] -> BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppCpp' extraArgs bi lbi =
|
||
|
case compilerFlavor (compiler lbi) of
|
||
|
GHC -> ppGhcCpp ghcProgram (>= Version [6,6] []) args bi lbi
|
||
|
GHCJS -> ppGhcCpp ghcjsProgram (const True) args bi lbi
|
||
|
_ -> ppCpphs args bi lbi
|
||
|
where cppArgs = getCppOptions bi lbi
|
||
|
args = cppArgs ++ extraArgs
|
||
|
|
||
|
ppGhcCpp :: Program -> (Version -> Bool)
|
||
|
-> [String] -> BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppGhcCpp program xHs extraArgs _bi lbi =
|
||
|
PreProcessor {
|
||
|
platformIndependent = False,
|
||
|
runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do
|
||
|
(prog, version, _) <- requireProgramVersion verbosity
|
||
|
program anyVersion (withPrograms lbi)
|
||
|
rawSystemProgram verbosity prog $
|
||
|
["-E", "-cpp"]
|
||
|
-- This is a bit of an ugly hack. We're going to
|
||
|
-- unlit the file ourselves later on if appropriate,
|
||
|
-- so we need GHC not to unlit it now or it'll get
|
||
|
-- double-unlitted. In the future we might switch to
|
||
|
-- using cpphs --unlit instead.
|
||
|
++ (if xHs version then ["-x", "hs"] else [])
|
||
|
++ [ "-optP-include", "-optP"++ (autogenModulesDir lbi </> cppHeaderName) ]
|
||
|
++ ["-o", outFile, inFile]
|
||
|
++ extraArgs
|
||
|
}
|
||
|
|
||
|
ppCpphs :: [String] -> BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppCpphs extraArgs _bi lbi =
|
||
|
PreProcessor {
|
||
|
platformIndependent = False,
|
||
|
runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do
|
||
|
(cpphsProg, cpphsVersion, _) <- requireProgramVersion verbosity
|
||
|
cpphsProgram anyVersion (withPrograms lbi)
|
||
|
rawSystemProgram verbosity cpphsProg $
|
||
|
("-O" ++ outFile) : inFile
|
||
|
: "--noline" : "--strip"
|
||
|
: (if cpphsVersion >= Version [1,6] []
|
||
|
then ["--include="++ (autogenModulesDir lbi </> cppHeaderName)]
|
||
|
else [])
|
||
|
++ extraArgs
|
||
|
}
|
||
|
|
||
|
ppHsc2hs :: BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppHsc2hs bi lbi =
|
||
|
PreProcessor {
|
||
|
platformIndependent = False,
|
||
|
runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do
|
||
|
(gccProg, _) <- requireProgram verbosity gccProgram (withPrograms lbi)
|
||
|
rawSystemProgramConf verbosity hsc2hsProgram (withPrograms lbi) $
|
||
|
[ "--cc=" ++ programPath gccProg
|
||
|
, "--ld=" ++ programPath gccProg ]
|
||
|
|
||
|
-- Additional gcc options
|
||
|
++ [ "--cflag=" ++ opt | opt <- programDefaultArgs gccProg
|
||
|
++ programOverrideArgs gccProg ]
|
||
|
++ [ "--lflag=" ++ opt | opt <- programDefaultArgs gccProg
|
||
|
++ programOverrideArgs gccProg ]
|
||
|
|
||
|
-- OSX frameworks:
|
||
|
++ [ what ++ "=-F" ++ opt
|
||
|
| isOSX
|
||
|
, opt <- nub (concatMap Installed.frameworkDirs pkgs)
|
||
|
, what <- ["--cflag", "--lflag"] ]
|
||
|
++ [ "--lflag=" ++ arg
|
||
|
| isOSX
|
||
|
, opt <- PD.frameworks bi ++ concatMap Installed.frameworks pkgs
|
||
|
, arg <- ["-framework", opt] ]
|
||
|
|
||
|
-- Note that on ELF systems, wherever we use -L, we must also use -R
|
||
|
-- because presumably that -L dir is not on the normal path for the
|
||
|
-- system's dynamic linker. This is needed because hsc2hs works by
|
||
|
-- compiling a C program and then running it.
|
||
|
|
||
|
++ [ "--cflag=" ++ opt | opt <- platformDefines lbi ]
|
||
|
|
||
|
-- Options from the current package:
|
||
|
++ [ "--cflag=-I" ++ dir | dir <- PD.includeDirs bi ]
|
||
|
++ [ "--cflag=" ++ opt | opt <- PD.ccOptions bi
|
||
|
++ PD.cppOptions bi ]
|
||
|
++ [ "--cflag=" ++ opt | opt <-
|
||
|
[ "-I" ++ autogenModulesDir lbi,
|
||
|
"-include", autogenModulesDir lbi </> cppHeaderName ] ]
|
||
|
++ [ "--lflag=-L" ++ opt | opt <- PD.extraLibDirs bi ]
|
||
|
++ [ "--lflag=-Wl,-R," ++ opt | isELF
|
||
|
, opt <- PD.extraLibDirs bi ]
|
||
|
++ [ "--lflag=-l" ++ opt | opt <- PD.extraLibs bi ]
|
||
|
++ [ "--lflag=" ++ opt | opt <- PD.ldOptions bi ]
|
||
|
|
||
|
-- Options from dependent packages
|
||
|
++ [ "--cflag=" ++ opt
|
||
|
| pkg <- pkgs
|
||
|
, opt <- [ "-I" ++ opt | opt <- Installed.includeDirs pkg ]
|
||
|
++ [ opt | opt <- Installed.ccOptions pkg ] ]
|
||
|
++ [ "--lflag=" ++ opt
|
||
|
| pkg <- pkgs
|
||
|
, opt <- [ "-L" ++ opt | opt <- Installed.libraryDirs pkg ]
|
||
|
++ [ "-Wl,-R," ++ opt | isELF
|
||
|
, opt <- Installed.libraryDirs pkg ]
|
||
|
++ [ "-l" ++ opt | opt <- Installed.extraLibraries pkg ]
|
||
|
++ [ opt | opt <- Installed.ldOptions pkg ] ]
|
||
|
++ ["-o", outFile, inFile]
|
||
|
}
|
||
|
where
|
||
|
pkgs = PackageIndex.topologicalOrder (packageHacks (installedPkgs lbi))
|
||
|
isOSX = case buildOS of OSX -> True; _ -> False
|
||
|
isELF = case buildOS of OSX -> False; Windows -> False; _ -> True;
|
||
|
packageHacks = case compilerFlavor (compiler lbi) of
|
||
|
GHC -> hackRtsPackage
|
||
|
GHCJS -> hackRtsPackage
|
||
|
_ -> id
|
||
|
-- We don't link in the actual Haskell libraries of our dependencies, so
|
||
|
-- the -u flags in the ldOptions of the rts package mean linking fails on
|
||
|
-- OS X (it's ld is a tad stricter than gnu ld). Thus we remove the
|
||
|
-- ldOptions for GHC's rts package:
|
||
|
hackRtsPackage index =
|
||
|
case PackageIndex.lookupPackageName index (PackageName "rts") of
|
||
|
[(_, [rts])]
|
||
|
-> PackageIndex.insert rts { Installed.ldOptions = [] } index
|
||
|
_ -> error "No (or multiple) ghc rts package is registered!!"
|
||
|
|
||
|
|
||
|
ppC2hs :: BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppC2hs bi lbi =
|
||
|
PreProcessor {
|
||
|
platformIndependent = False,
|
||
|
runPreProcessor = \(inBaseDir, inRelativeFile)
|
||
|
(outBaseDir, outRelativeFile) verbosity -> do
|
||
|
(c2hsProg, _, _) <- requireProgramVersion verbosity
|
||
|
c2hsProgram (orLaterVersion (Version [0,15] []))
|
||
|
(withPrograms lbi)
|
||
|
(gccProg, _) <- requireProgram verbosity gccProgram (withPrograms lbi)
|
||
|
rawSystemProgram verbosity c2hsProg $
|
||
|
|
||
|
-- Options from the current package:
|
||
|
[ "--cpp=" ++ programPath gccProg, "--cppopts=-E" ]
|
||
|
++ [ "--cppopts=" ++ opt | opt <- getCppOptions bi lbi ]
|
||
|
++ [ "--include=" ++ outBaseDir ]
|
||
|
|
||
|
-- Options from dependent packages
|
||
|
++ [ "--cppopts=" ++ opt
|
||
|
| pkg <- pkgs
|
||
|
, opt <- [ "-I" ++ opt | opt <- Installed.includeDirs pkg ]
|
||
|
++ [ opt | opt@('-':c:_) <- Installed.ccOptions pkg
|
||
|
, c `elem` "DIU" ] ]
|
||
|
--TODO: install .chi files for packages, so we can --include
|
||
|
-- those dirs here, for the dependencies
|
||
|
|
||
|
-- input and output files
|
||
|
++ [ "--output-dir=" ++ outBaseDir
|
||
|
, "--output=" ++ outRelativeFile
|
||
|
, inBaseDir </> inRelativeFile ]
|
||
|
}
|
||
|
where
|
||
|
pkgs = PackageIndex.topologicalOrder (installedPkgs lbi)
|
||
|
|
||
|
--TODO: perhaps use this with hsc2hs too
|
||
|
--TODO: remove cc-options from cpphs for cabal-version: >= 1.10
|
||
|
getCppOptions :: BuildInfo -> LocalBuildInfo -> [String]
|
||
|
getCppOptions bi lbi
|
||
|
= platformDefines lbi
|
||
|
++ cppOptions bi
|
||
|
++ ["-I" ++ dir | dir <- PD.includeDirs bi]
|
||
|
++ [opt | opt@('-':c:_) <- PD.ccOptions bi, c `elem` "DIU"]
|
||
|
|
||
|
platformDefines :: LocalBuildInfo -> [String]
|
||
|
platformDefines lbi =
|
||
|
case compilerFlavor comp of
|
||
|
GHC ->
|
||
|
["-D__GLASGOW_HASKELL__=" ++ versionInt version] ++
|
||
|
["-D" ++ os ++ "_BUILD_OS=1"] ++
|
||
|
["-D" ++ arch ++ "_BUILD_ARCH=1"] ++
|
||
|
map (\os' -> "-D" ++ os' ++ "_HOST_OS=1") osStr ++
|
||
|
map (\arch' -> "-D" ++ arch' ++ "_HOST_ARCH=1") archStr
|
||
|
GHCJS ->
|
||
|
compatGlasgowHaskell ++
|
||
|
["-D__GHCJS__=" ++ versionInt version] ++
|
||
|
["-D" ++ os ++ "_BUILD_OS=1"] ++
|
||
|
["-D" ++ arch ++ "_BUILD_ARCH=1"] ++
|
||
|
map (\os' -> "-D" ++ os' ++ "_HOST_OS=1") osStr ++
|
||
|
map (\arch' -> "-D" ++ arch' ++ "_HOST_ARCH=1") archStr
|
||
|
JHC -> ["-D__JHC__=" ++ versionInt version]
|
||
|
HaskellSuite {} ->
|
||
|
["-D__HASKELL_SUITE__"] ++
|
||
|
map (\os' -> "-D" ++ os' ++ "_HOST_OS=1") osStr ++
|
||
|
map (\arch' -> "-D" ++ arch' ++ "_HOST_ARCH=1") archStr
|
||
|
_ -> []
|
||
|
where
|
||
|
comp = compiler lbi
|
||
|
Platform hostArch hostOS = hostPlatform lbi
|
||
|
version = compilerVersion comp
|
||
|
compatGlasgowHaskell =
|
||
|
maybe [] (\v -> ["-D__GLASGOW_HASKELL__=" ++ versionInt v])
|
||
|
(compilerCompatVersion GHC comp)
|
||
|
-- TODO: move this into the compiler abstraction
|
||
|
-- FIXME: this forces GHC's crazy 4.8.2 -> 408 convention on all
|
||
|
-- the other compilers. Check if that's really what they want.
|
||
|
versionInt :: Version -> String
|
||
|
versionInt (Version { versionBranch = [] }) = "1"
|
||
|
versionInt (Version { versionBranch = [n] }) = show n
|
||
|
versionInt (Version { versionBranch = n1:n2:_ })
|
||
|
= -- 6.8.x -> 608
|
||
|
-- 6.10.x -> 610
|
||
|
let s1 = show n1
|
||
|
s2 = show n2
|
||
|
middle = case s2 of
|
||
|
_ : _ : _ -> ""
|
||
|
_ -> "0"
|
||
|
in s1 ++ middle ++ s2
|
||
|
osStr = case hostOS of
|
||
|
Linux -> ["linux"]
|
||
|
Windows -> ["mingw32"]
|
||
|
OSX -> ["darwin"]
|
||
|
FreeBSD -> ["freebsd"]
|
||
|
OpenBSD -> ["openbsd"]
|
||
|
NetBSD -> ["netbsd"]
|
||
|
DragonFly -> ["dragonfly"]
|
||
|
Solaris -> ["solaris2"]
|
||
|
AIX -> ["aix"]
|
||
|
HPUX -> ["hpux"]
|
||
|
IRIX -> ["irix"]
|
||
|
HaLVM -> []
|
||
|
IOS -> ["ios"]
|
||
|
Ghcjs -> ["ghcjs"]
|
||
|
OtherOS _ -> []
|
||
|
archStr = case hostArch of
|
||
|
I386 -> ["i386"]
|
||
|
X86_64 -> ["x86_64"]
|
||
|
PPC -> ["powerpc"]
|
||
|
PPC64 -> ["powerpc64"]
|
||
|
Sparc -> ["sparc"]
|
||
|
Arm -> ["arm"]
|
||
|
Mips -> ["mips"]
|
||
|
SH -> []
|
||
|
IA64 -> ["ia64"]
|
||
|
S390 -> ["s390"]
|
||
|
Alpha -> ["alpha"]
|
||
|
Hppa -> ["hppa"]
|
||
|
Rs6000 -> ["rs6000"]
|
||
|
M68k -> ["m68k"]
|
||
|
Vax -> ["vax"]
|
||
|
JavaScript -> ["javascript"]
|
||
|
OtherArch _ -> []
|
||
|
|
||
|
ppHappy :: BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppHappy _ lbi = pp { platformIndependent = True }
|
||
|
where pp = standardPP lbi happyProgram (hcFlags hc)
|
||
|
hc = compilerFlavor (compiler lbi)
|
||
|
hcFlags GHC = ["-agc"]
|
||
|
hcFlags GHCJS = ["-agc"]
|
||
|
hcFlags _ = []
|
||
|
|
||
|
ppAlex :: BuildInfo -> LocalBuildInfo -> PreProcessor
|
||
|
ppAlex _ lbi = pp { platformIndependent = True }
|
||
|
where pp = standardPP lbi alexProgram (hcFlags hc)
|
||
|
hc = compilerFlavor (compiler lbi)
|
||
|
hcFlags GHC = ["-g"]
|
||
|
hcFlags GHCJS = ["-g"]
|
||
|
hcFlags _ = []
|
||
|
|
||
|
standardPP :: LocalBuildInfo -> Program -> [String] -> PreProcessor
|
||
|
standardPP lbi prog args =
|
||
|
PreProcessor {
|
||
|
platformIndependent = False,
|
||
|
runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->
|
||
|
rawSystemProgramConf verbosity prog (withPrograms lbi)
|
||
|
(args ++ ["-o", outFile, inFile])
|
||
|
}
|
||
|
|
||
|
-- |Convenience function; get the suffixes of these preprocessors.
|
||
|
ppSuffixes :: [ PPSuffixHandler ] -> [String]
|
||
|
ppSuffixes = map fst
|
||
|
|
||
|
-- |Standard preprocessors: GreenCard, c2hs, hsc2hs, happy, alex and cpphs.
|
||
|
knownSuffixHandlers :: [ PPSuffixHandler ]
|
||
|
knownSuffixHandlers =
|
||
|
[ ("gc", ppGreenCard)
|
||
|
, ("chs", ppC2hs)
|
||
|
, ("hsc", ppHsc2hs)
|
||
|
, ("x", ppAlex)
|
||
|
, ("y", ppHappy)
|
||
|
, ("ly", ppHappy)
|
||
|
, ("cpphs", ppCpp)
|
||
|
]
|