Commit 2ee2c870 authored by Simon Marlow's avatar Simon Marlow
Browse files

Add auto-generated CPP macros for package version testing

Now when using CPP you get 

   MIN_VERSION_<package>(A,B,C)

for each <package> in build-depends, which is true if the version of
<package> in use is >= A.B.C, using the normal ordering on version
numbers.

This is done by auto-generating a header file
dist/build/autogen/cabal_macros.h, and passing a -include flag when
running CPP.
parent 54eb6a8e
......@@ -62,7 +62,7 @@ import Distribution.PackageDescription
( PackageDescription(..), BuildInfo(..),
Executable(..), Library(..) )
import Distribution.Package
( Package(..), packageName, packageVersion )
( PackageIdentifier, Package(..), packageName, packageVersion )
import qualified Distribution.ModuleName as ModuleName
import Distribution.Simple.Setup ( CopyDest(..), BuildFlags(..),
MakefileFlags(..), fromFlag )
......@@ -71,18 +71,22 @@ import Distribution.Simple.LocalBuildInfo
( LocalBuildInfo(..),
InstallDirs(..), absoluteInstallDirs,
prefixRelativeInstallDirs )
import Distribution.Simple.BuildPaths ( autogenModuleName, autogenModulesDir )
import Distribution.Simple.BuildPaths
( autogenModulesDir, autogenModuleName,
cppHeaderName )
import Distribution.Simple.Configure
( localBuildInfoFile )
import Distribution.Simple.Utils
( createDirectoryIfMissingVerbose, die, setupMessage, writeUTF8File )
import Distribution.System
import Distribution.Version ( versionBranch )
import System.FilePath ( (</>), (<.>), pathSeparator )
import Data.Maybe ( maybeToList, fromJust, isNothing )
import Control.Monad ( unless, when )
import System.Directory ( getModificationTime, doesFileExist )
import Text.Printf ( printf )
import qualified Distribution.Simple.GHC as GHC
import qualified Distribution.Simple.JHC as JHC
......@@ -151,9 +155,46 @@ initialBuildSteps distPref pkg_descr lbi verbosity suffixes = do
-- construct and write the Paths_<pkg>.hs file
createDirectoryIfMissingVerbose verbosity True (autogenModulesDir lbi)
buildPathsModule distPref pkg_descr lbi
buildCPPHeader distPref pkg_descr lbi
preprocessSources pkg_descr lbi False verbosity suffixes
-- ------------------------------------------------------------
-- * Building cabal_macros.h
-- ------------------------------------------------------------
buildCPPHeader :: FilePath -> PackageDescription -> LocalBuildInfo -> IO ()
buildCPPHeader distPref _pkg_descr lbi =
let
cpp_header_filepath = autogenModulesDir lbi </> cppHeaderName
preface = "/* DO NOT EDIT: This file is automatically generated by Cabal */"
version_macro :: PackageIdentifier -> String
version_macro pkgid =
printf ("#define MIN_VERSION_%s(major1,major2,minor) \\\n" ++
" (major1) < %d || \\\n" ++
" (major1) == %d && (major2) < %d || \\\n" ++
" (major1) == %d && (major2) == %d && (minor) <= %d")
(display (packageName pkgid)) major1
major1 major2
major1 major2 minor
where
vs = versionBranch (packageVersion pkgid)
(major1:major2:minor:_) = vs ++ repeat 0
contents = unlines (preface : map version_macro (packageDeps lbi))
in do
btime <- getModificationTime (localBuildInfoFile distPref)
exists <- doesFileExist cpp_header_filepath
ptime <- if exists
then getModificationTime cpp_header_filepath
else return btime
if btime >= ptime
then writeFile cpp_header_filepath contents
else return ()
-- ------------------------------------------------------------
-- * Building Paths_<pkg>.hs
-- ------------------------------------------------------------
......
......@@ -46,6 +46,7 @@ module Distribution.Simple.BuildPaths (
autogenModulesDir,
autogenModuleName,
cppHeaderName,
haddockName,
mkLibName,
......@@ -91,6 +92,8 @@ haddockPref distPref pkg_descr
autogenModulesDir :: LocalBuildInfo -> String
autogenModulesDir lbi = buildDir lbi </> "autogen"
cppHeaderName :: String
cppHeaderName = "cabal_macros.h"
-- |The name of the auto-generated module associated with a package
autogenModuleName :: PackageDescription -> ModuleName
......
......@@ -673,10 +673,12 @@ ghcOptions lbi bi odir
++ ["-i" ++ odir]
++ ["-i" ++ l | l <- nub (hsSourceDirs bi)]
++ ["-i" ++ autogenModulesDir lbi]
++ ["-I" ++ autogenModulesDir lbi]
++ ["-I" ++ odir]
++ ["-I" ++ dir | dir <- PD.includeDirs bi]
++ ["-optP" ++ opt | opt <- cppOptions bi]
++ ["-optc" ++ opt | opt <- PD.ccOptions bi]
++ [ "-optP-include", "-optP"++ (autogenModulesDir lbi </> cppHeaderName) ]
++ [ "-#include \"" ++ inc ++ "\"" | inc <- PD.includes bi ]
++ [ "-odir", odir, "-hidir", odir ]
++ (if compilerVersion c >= Version [6,8] []
......
......@@ -71,7 +71,7 @@ import qualified Distribution.Simple.PackageSet as PackageSet
import Distribution.Simple.Compiler
( CompilerFlavor(..), Compiler(..), compilerFlavor, compilerVersion )
import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..))
import Distribution.Simple.BuildPaths (autogenModulesDir)
import Distribution.Simple.BuildPaths (autogenModulesDir,cppHeaderName)
import Distribution.Simple.Utils
( createDirectoryIfMissingVerbose, withUTF8FileContents, writeUTF8File
, die, setupMessage, intercalate, copyFileVerbose
......@@ -319,6 +319,7 @@ ppGhcCpp extraArgs _bi lbi =
-- using cpphs --unlit instead.
++ (if ghcVersion >= Version [6,6] [] then ["-x", "hs"] else [])
++ (if use_optP_P lbi then ["-optP-P"] else [])
++ [ "-optP-include", "-optP"++ (autogenModulesDir lbi </> cppHeaderName) ]
++ ["-o", outFile, inFile]
++ extraArgs
}
......@@ -330,11 +331,16 @@ ppCpphs extraArgs _bi lbi =
PreProcessor {
platformIndependent = False,
runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->
rawSystemProgramConf verbosity cpphsProgram (withPrograms lbi) $
rawSystemProgram verbosity cpphsProg $
("-O" ++ outFile) : inFile
: "--noline" : "--strip"
: extraArgs
: (if cpphsVersion >= Version [1,6] []
then ["--include="++ (autogenModulesDir lbi </> cppHeaderName)]
else [])
++ extraArgs
}
where Just cpphsProg = lookupProgram cpphsProgram (withPrograms lbi)
Just cpphsVersion = programVersion cpphsProg
-- Haddock versions before 0.8 choke on #line and #file pragmas. Those
-- pragmas are necessary for correct links when we preprocess. So use
......
......@@ -1681,6 +1681,47 @@ ld-options: -L/usr/X11R6/lib</programlisting>
</note>
</sect2>
<sect2 id="cpp">
<title>Conditional compilation</title>
<para>Sometimes you want to write code that works with more than
one version of a dependency. You can specify a range of
versions for the depenency in
the <literal>build-depends</literal>, but how do you then write
the code that can use different versions of the API?</para>
<para>Haskell lets you preprocess your code using the C
preprocessor (either the real C preprocessor, or
<literal>cpphs</literal>). To enable this,
add <literal>extensions: CPP</literal> to your package
description. When using CPP, Cabal provides some pre-defined
macros to let you test the version of dependent packages; for
example, suppose your package works with either version 3 or
version 4 of the <literal>base</literal> package, you could
select the available version in your Haskell modules like
this:</para>
<programlisting>
#if MIN_VERSION_base(4,0,0)
... code that works with base-4 ...
#else
... code that works with base-3 ...
#endif
</programlisting>
<para>In general, Cabal supplies a
macro <literal>MIN_VERSION_<replaceable>package</replaceable>(A,B,C)</literal>
for each package depended on via <literal>build-depends</literal>.
This macro is true if the actual version of the package in use is
greater than or equal to <literal>A.B.C</literal> (using the
conventional ordering on version numbers, which is lexicographic on
the sequence, but numeric on each component, so for example 1.2.0 is
greater than 1.0.3).</para>
<para>Cabal places the definitions of these macros into an
automatically-generated header file, which is included when
preprocessing Haskell source code by passing options to the C
preprocessor.</para>
</sect2>
<sect2 id="complex-packages">
<title>More complex packages</title>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment