Commit a9c93bdd authored by Edward Z. Yang's avatar Edward Z. Yang Committed by Ben Gamari

Implement MIN_VERSION and VERSION macros natively in GHC.

Test Plan: validate

Reviewers: austin, thomie, bgamari

Reviewed By: thomie

Subscribers: thomie

Differential Revision: https://phabricator.haskell.org/D1349

GHC Trac Issues: #10970
parent 499ce291
......@@ -74,6 +74,7 @@ import Data.List ( isSuffixOf )
import Data.Maybe
import Data.Char
import Data.Time
import Data.Version
-- ---------------------------------------------------------------------------
-- Pre-process
......@@ -2049,6 +2050,20 @@ doCpp dflags raw input_fn output_fn = do
, "-include", ghcVersionH
]
-- MIN_VERSION macros
let uids = explicitPackages (pkgState dflags)
pkgs = catMaybes (map (lookupPackage dflags) uids)
mb_macro_include <-
-- Only generate if we have (1) we have set -hide-all-packages
-- (so we don't generate a HUGE macro file of things we don't
-- care about but are exposed) and (2) we actually have packages
-- to write macros for!
if gopt Opt_HideAllPackages dflags && not (null pkgs)
then do macro_stub <- newTempName dflags "h"
writeFile macro_stub (generatePackageVersionMacros pkgs)
return [SysTools.FileOption "-include" macro_stub]
else return []
cpp_prog ( map SysTools.Option verbFlags
++ map SysTools.Option include_paths
++ map SysTools.Option hsSourceCppOpts
......@@ -2058,6 +2073,7 @@ doCpp dflags raw input_fn output_fn = do
++ map SysTools.Option hscpp_opts
++ map SysTools.Option sse_defs
++ map SysTools.Option avx_defs
++ mb_macro_include
-- Set the language mode to assembler-with-cpp when preprocessing. This
-- alleviates some of the C99 macro rules relating to whitespace and the hash
-- operator, which we tend to abuse. Clang in particular is not very happy
......@@ -2087,6 +2103,35 @@ getBackendDefs dflags | hscTarget dflags == HscLlvm = do
getBackendDefs _ =
return []
-- ---------------------------------------------------------------------------
-- Macros (cribbed from Cabal)
generatePackageVersionMacros :: [PackageConfig] -> String
generatePackageVersionMacros pkgs = concat
[ "/* package " ++ sourcePackageIdString pkg ++ " */\n"
++ generateMacros "" pkgname version
| pkg <- pkgs
, let version = packageVersion pkg
pkgname = map fixchar (packageNameString pkg)
]
fixchar :: Char -> Char
fixchar '-' = '_'
fixchar c = c
generateMacros :: String -> String -> Version -> String
generateMacros prefix name version =
concat
["#define ", prefix, "VERSION_",name," ",show (showVersion version),"\n"
,"#define MIN_", prefix, "VERSION_",name,"(major1,major2,minor) (\\\n"
," (major1) < ",major1," || \\\n"
," (major1) == ",major1," && (major2) < ",major2," || \\\n"
," (major1) == ",major1," && (major2) == ",major2," && (minor) <= ",minor,")"
,"\n\n"
]
where
(major1:major2:minor:_) = map show (versionBranch version ++ repeat 0)
-- ---------------------------------------------------------------------------
-- join object files into a single relocatable object file, using ld -r
......
......@@ -7,7 +7,7 @@ module Packages (
module PackageConfig,
-- * Reading the package config, and processing cmdline args
PackageState(preloadPackages),
PackageState(preloadPackages, explicitPackages),
emptyPackageState,
initPackages,
readPackageConfigs,
......@@ -245,6 +245,10 @@ data PackageState = PackageState {
-- is always mentioned before the packages it depends on.
preloadPackages :: [UnitId],
-- | Packages which we explicitly depend on (from a command line flag).
-- We'll use this to generate version macros.
explicitPackages :: [UnitId],
-- | This is a full map from 'ModuleName' to all modules which may possibly
-- be providing it. These providers may be hidden (but we'll still want
-- to report them in error messages), or it may be an ambiguous import.
......@@ -255,6 +259,7 @@ emptyPackageState :: PackageState
emptyPackageState = PackageState {
pkgIdMap = emptyUFM,
preloadPackages = [],
explicitPackages = [],
moduleToPkgConfAll = Map.empty
}
......@@ -961,6 +966,10 @@ mkPackageState dflags0 pkgs0 preload0 = do
let pstate = PackageState{
preloadPackages = dep_preload,
explicitPackages = foldUFM (\pkg xs ->
if elemUFM (packageConfigId pkg) vis_map
then packageConfigId pkg : xs
else xs) [] pkg_db,
pkgIdMap = pkg_db,
moduleToPkgConfAll = mkModuleToPkgConfAll dflags pkg_db vis_map
}
......
......@@ -340,6 +340,22 @@ defined by your local GHC installation, the following trick is useful:
architecture, wherearch is the name of the current architecture
(eg. ``i386``, ``x86_64``, ``powerpc``, ``sparc``, etc.).
``VERSION_pkgname``
This macro is available starting GHC 8.0. It is defined for every
exposed package, but only if the ``-hide-all-packages`` flag
is set. This macro expands to a string recording the
version of ``pkgname`` that is exposed for module import.
It is identical in behavior to the ``VERSION_pkgname`` macros
that Cabal defines.
``MIN_VERSION_pkgname(x,y,z)``
This macro is available starting GHC 8.0. It is defined for every
exposed package, but only if the ``-hide-all-packages`` flag
is set. This macro is provided for convenience to write CPP
conditionals testing if a package version is ``x.y.z`` or
less. It is identical in behavior to the ``MIN_VERSION_pkgname``
macros that Cabal defines.
.. _cpp-string-gaps:
CPP and string gaps
......
......@@ -532,6 +532,7 @@ mk/ghcconfig*_bin_ghc*.exe.mk
/tests/driver/Hello062a.hs
/tests/driver/Hello062b.hs
/tests/driver/Hello062c.hs
/tests/driver/T10970
/tests/driver/T1959/E.hs
/tests/driver/T1959/prog
/tests/driver/T3007/A/Setup
......@@ -631,8 +632,8 @@ mk/ghcconfig*_bin_ghc*.exe.mk
/tests/driver/recomp013/C.hs
/tests/driver/recomp014/A.hs
/tests/driver/recomp014/A1.hs
/tests/driver/recomp014/B.hsig
/tests/driver/recomp014/B.hs-boot
/tests/driver/recomp014/B.hsig
/tests/driver/recomp014/C.hs
/tests/driver/recomp014/recomp014
/tests/driver/retc001/B.hs
......
{-# LANGUAGE CPP #-}
main = do
putStrLn VERSION_containers
#if MIN_VERSION_base(3,0,0)
putStrLn "OK"
#endif
{-# LANGUAGE CPP #-}
main = do
putStrLn VERSION_containers
#if MIN_VERSION_base(3,0,0)
putStrLn "OK"
#endif
T10970a.hs:4:0: error:
error: missing binary operator before token "("
#if MIN_VERSION_base(3,0,0)
^
`gcc' failed in phase `C pre-processor'. (Exit code: 1)
......@@ -456,3 +456,6 @@ test('T365',
test('T9360a', normal, run_command, ['{compiler} --interactive -e ""'])
test('T9360b', normal, run_command, ['{compiler} -e "" --interactive'])
test('T10970', normal, compile_and_run, ['-hide-all-packages -package base -package containers'])
test('T10970a', normal, compile_fail, [''])
Markdown is supported
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